Home > other >  Using Dynamic Interfaces in Angular
Using Dynamic Interfaces in Angular

Time:10-19

I'm trying map out a dynamic interface for an Angular project. I was able to get some help on here previously with using a dynamic interface, but using it in Angular seems to be giving me some problems.

I'm trying to get back some video game data using an API and the top root level key changes, based on the platform name...

// Possible platforms the user can search
enum Platforms {
    'ps3', 
    'ps4', 
    'ps5', 
    'xbox-360',
    'xbox-one', 
    'xbox-series-x', 
    '3ds',  
    'switch', 
    'pc'
}

// This interface will map out the games being returned by platform
export type GamesByPlatform = {
    [key in Platforms]: {
        data: Game[];
    }
}

In my home component, I try to access those games by subscribing to the observable returned from a game service method. I want to take that interface and access the Game array that it carries. Also, 'ps4' is being hard-coded in the game service method for testing purposes. I'm mainly concerned with working with the interface I created.

Game Service...

getGameList(): Observable<GamesByPlatform> {
    let params = new HttpParams().set('platform', 'ps4')
      .set('count', '16');

    return this.http.get<GamesByPlatform>(`${env.BASE_URL}`, {
      params: params
    });
  }

Home Component

import { Component, OnInit } from '@angular/core';
import { GamesService } from '../games.service';
import { Game, GamesByPlatform } from '../shared/models/Game';


@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
  games: Game[] = [];

  constructor(private gameService: GamesService) { }

  ngOnInit(): void {
    this.gameService.getGameList()
      .subscribe((results: GamesByPlatform) => {
        this.games = results
      })
  }

}

I'm not sure how to work with that dynamic interface in the home component. If I use dot notation, I get the option to use results[0], results[1], results[2] etc. Using any of these options yields a result of undefined. I most likely need to take a deep dive into Typescript, but I'm not quite sure how to use that dynamic interface in my home component.

CodePudding user response:

Explanation

The problem is that you are using an enum like this:

enum Platforms {
    'ps3', 
    'ps4', 
    'ps5', 
    'xbox-360',
    'xbox-one', 
    'xbox-series-x', 
    '3ds',  
    'switch', 
    'pc'
}

which is equivalent to:

enum Platforms {
    'ps3': 0, 
    'ps4': 1, 
    'ps5': 2, 
    'xbox-360': 3,
    'xbox-one': 4, 
    'xbox-series-x': 5, 
    '3ds': 6,  
    'switch': 7, 
    'pc': 8
}

Your type

export type GamesByPlatform = {
    [key in Platforms]: {
        data: Game[];
    }
}

is therefore equivalent to

export type GamesByPlatform = {
    0: { data: Game[] },
    1: { data: Game[] },
    2: { data: Game[] },
    3: { data: Game[] },
    4: { data: Game[] },
    5: { data: Game[] },
    6: { data: Game[] },
    7: { data: Game[] },
    8: { data: Game[] }
}

Which is most likely not what you want. There are some ways around this.

Solution 1: String literal type

Instead of using an enum, you could use a string literal type for your type like this:

export type Platforms = 'ps3' 
    | 'ps4' 
    | 'ps5', 
    | 'xbox-360',
    | 'xbox-one', 
    | 'xbox-series-x', 
    | '3ds',  
    | 'switch', 
    | 'pc'

export type GamesByPlatform = {
    [key in Platforms]: {
        data: Game[];
    }
}

Solution 2: Explicit enum values

Another way to achieve what you want is giving your enum explicit values.

enum Platforms {
    'ps3': 'ps3', 
    'ps4': 'ps4', 
    'ps5': 'ps5', 
    'xbox-360': 'xbox-360',
    'xbox-one': 'xbox-one, 
    'xbox-series-x': 'xbox-series-x', 
    '3ds': '3ds',  
    'switch': 'switch', 
    'pc': 'pc'
}
  • Related