How do we create multiple collections using Angular-in-memory-web-api? Not an issue with single collection. But I\'m not able to implement it f
Just return it an object with both arrays. In the example from Angular, you see something like
createDb() {
let heroes = [ .. ]
return { heroes }
}
If you don't already know this, { heroes }
is just shorthand for writing { heroes: heroes }
. So if you have two collections, then just add it as a another property
createDb() {
let heroes = [ .. ];
let crises = [ .. ];
return { heroes, crises };
// or { heroes: heroes, crises: crises }
}
The name of the property returned will be used for the path in the URL. So you can use
/api/heroes/1
/api/crises/1
The approach described in the Paul's answer is correct, however there is one detail I missed which I like to add: How do you specify genId
correctly, so it works for both collections?
The answer is referring to the "Heroes" example written in TypeScript (a superset of JavaScript), specifically the HTTP chapter.
There, a table heroes
is simulated by implementing:
export class InMemoryDataService implements InMemoryDbService {
createDb() {
const heroes = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
// ...
{ id: 20, name: 'Tornado' }
];
return {heroes};
}
// Overrides the genId method to ensure that a hero always has an id.
// If the heroes array is empty,
// the method below returns the initial number (11).
// if the heroes array is not empty, the method below returns the highest
// hero id + 1.
genId(heroes: Hero[]): number {
return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
}
}
Now, if you add a 2nd collection crises
as shown in his answer, i.e.:
createDb() {
let heroes = [ { ... }, ... ];
let crises = [ { ... }, ... ];
return { heroes, crises };
// or { heroes: heroes, crises: crises }
}
how do you provide genId
for both collections (provided they are of type Hero
and Crises
)? Overloading, as you would do it in C#
does not work in TypeScript, it would throw an error ("Duplicate function implementation").
Solution:
I found out, that you can solve this issue with TypeScript's Generics as follows. Replace the otiginal genId
function by the following generic version:
genId<T extends Hero | Crises>(myTable: T[]): number {
return myTable.length > 0 ? Math.max(...myTable.map(t => t.id)) + 1 : 11;
}
What's important here is the part <T extends Hero | Crises>
: It means that type T
can be either Hero
or Crises
: So it will be invoked if the parameter passed is either of type Hero[]
or Crises[]
.
With that knowledge, adding a 3rd, 4th, ... class is simple: Just append the class. Say we want to add the class SuperHero
, then you just append it via | SuperHero
, so it would look like:
genId<T extends Hero | Crises | SuperHero>(myTable: T[]): number {
return myTable.length > 0 ? Math.max(...myTable.map(t => t.id)) + 1 : 11;
}
Note: As a prerequisite, all classes (Hero
, Crises
and SuperHero
) need to have a numeric id
property declared.
Useful links:
HTTP chapter of Angular Tutorial
In-Memory-Web-API: Custom genId
Can You Specify Multiple Type Constraints For TypeScript Generics?
Adding to this discussion, I had to use Partial as it kept on saying I was missing an id when completing the Tour of Heroes Lesson 06. I used the message service to see how far the code went before it stopped working, so you can ignore the this.log lines, but you can add them to your own project to help with debugging. First, I updated the addHero as follows:
addHero(hero: string): void {
this.log(`In addHero of HeroesComponent, name is: ${hero}`);
if (!hero)
{
this.log(`In if(!hero.name) of add hero: ${hero}`);
return;
}
this.heroService.addHero({ name: hero } as Partial<Hero>)
.subscribe(hero => {
this.heroes.push(hero);
});
}
And then I updated the heroes.service.ts file to use the partial hero and allow the code to generate its own id from the in-memory-data.service.ts:
addHero(hero: Partial): Observable {
this.log(`In addHero: heroeName is: ${hero.name}`);
return this.http.post(this.heroesUrl, hero, this.httpOptions).pipe(
tap((newHero: Hero) =>
this.log(`added hero with string id=${newHero.id}`)),
catchError(this.handleError("Error in addHero")));
}
As far as I can see, it is now working as expected. There may be some more code to update, but this should help anyone with the following error message:
ERROR in src/app/heroes/heroes.component.ts:52:30 - error TS2352: Conversion of type '{ name: never; }' to type 'Hero' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Property 'id' is missing in type '{ name: never; }' but required in type 'Hero'.