1
votes

I'm learning about the official angular tutorial (https://angular.io/tutorial/toh-pt4). Excuse me for my low level :(

I'm trying to modify a service that returns the list of heroes ... As observables objects of rxjs, because it is the most efficient way to do it

hero.service.ts
==============

import {Injectable} from '@ angular / core';

import {Observable, of} from 'rxjs';

import {Hero} from './hero';
import {HEROES} from './mock-heroes';
import {MessageService} from './message.service';

@Injectable ({
  providedIn: 'root',
})
export class HeroService {

  constructor (private messageService: MessageService) {}

  getHeroes (): Observable <Hero []> {
    // TODO: send the message _after_ fetching the heroes
    this.messageService.add ('HeroService: fetched heroes');
    return of (HEROES);
  }
}

mock-heroes
===========

import {Hero} from './hero';

export const HEROES: Hero [] = [
  {id: 11, name: 'Mr. Nice '},
  {id: 12, name: 'Narco'},
  {id: 13, name: 'Bombast'},
  {id: 14, name: 'Celeritas'},
  {id: 15, name: 'Magneta'},
  {id: 16, name: 'RubberMan'},
  {id: 17, name: 'Dynama'},
  {id: 18, name: 'Dr IQ'},
  {id: 19, name: 'Magma'},
  {id: 20, name: 'Tornado'}
];

I want to have a service that returns only a hero when I pass an id

import {Injectable} from '@ angular / core';
import {Hero} from './hero';
import {HEROES} from './mock-heroes';
import {Observable, of} from 'rxjs';
import {MessageService} from './message.service';
import {map} from 'rxjs / operators';

@Injectable ({
  providedIn: 'root'
})
export class HeroService {

  private heroes: Observable <Hero []>;

  getHeroes (): Observable <Hero []> {
    this.messageService.add ('HeroService: fetched heroes');
    // return of (HEROES);
    this.heroes = of (HEROES);
    return this.heroes;
  }

  getHeroeById (id: number): Observable <Hero> {
    this.messageService.add ('A hero is queried with id' + id);
    

    return this.heroes.pipe (map (x => x.id === id));
  }

  constructor (private messageService: MessageService) {}

 / * getHeroes (): Hero [] {
    return HEROES;
  } * /

  //builder() { }
}

ERROR in src / app / hero.service.ts (26,5): error TS2322: Type 'Observable ' is not assignable to type 'Observable '.   Type 'boolean' is not assignable to type 'Hero'. src / app / hero.service.ts (26.40): TS2339 error: Property 'id' does not exist on type 'Hero []'.

Can you help me ?

3

3 Answers

2
votes

You have a couple options. You can use Array.find() or Array.filter()

Array.filter()

getHeroeById (id: number): Observable <Hero> {
  this.messageService.add('A hero is queried with id: ' + id);

  // Filter heroes to get heroById
  return this.heroes.filter(x => x.id == id)[0];
}

Array.find()

getHeroeById (id: number): Observable <Hero> {
  this.messageService.add('A hero is queried with id: ' + id);

  // Find hero by Id.
  return this.heroes.find(x => x.id == this.personId);
}
1
votes

You want to return the hero in the hero list with the matching id, try replacing

return this.heroes.pipe (map (x => x.id === id));

by

return this.heroes.pipe (map (heroes => heroes.find( hero => hero.id === id));
1
votes

your issue is this line:

return this.heroes.pipe (map (x => x.id === id));

you are using the rx map operator, which operates on and transforms the items in the stream, and the stream item in the case is the actual array of heroes. You're returning the result of the test of whether some nonexistent id property of the array is equal to the id parameter, which is a boolean value. This is why you're seeing the errors you are.

You should use the array find operator on the array in stream in this case since you want to find that item in the array, like so:

return this.heroes.pipe (map (heroes => heroes.find(h => h.id === id)));

this way, you're returning the actual result of the array find operation within the rx map transform operation.

Next thing, you don't initialize your heroes obersvable until getHeroes() is called. This may be intentional, but it seems like you should instead do it in the constructor so getHeroeById() can be safely called first.