0

I've been trying to make a simple relation between an angular 2 component and service, but I can't figure it out how to do so using Observable.

My component HeroViewerComponent subscribes to the router parameter id and passes it to a service HeroService to get Hero when the service get it from the server. HeroService has a method called getHeroSubscription that takes an Observable<number> as a parameter and return Observable<Hero>. But it doesn't work.

Here is my HeroViewerComponent in hero-viewer.component.ts:

import { Component, OnInit, OnDestroy } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Observable } from "rxjs/Observable";
import { Observer } from "rxjs/Observer";
import { Subscription } from "rxjs/Subscription";

import { HeroService } from "./hero.service";
import { Hero } from "./hero";

@Component({
    providers: [HeroService]
})
export class HeroViewerComponent implements OnInit, OnDestroy {
    private routeParamsSubscription:Subscription;

    // to the service
    private routeParamsObservable:Observable<number>;

    // the subscriber of the route ID param
    private routeParamsObserver:Observer<number>;

    // from the service
    private heroSubscription:Subscription;

    // the current hero
    hero:Hero;

    constructor(private route:ActivatedRoute, private heroService:HeroService) {}

    ngOnInit() {
        this.routeParamsObservable = new Observable<number>((observer:Observer<number>) => this.routeParamsObserver = observer);

        this.heroSubscription = this.heroService
            .getHeroSubscription(this.routeParamsObservable)
            .subscribe(
                hero => this.hero = hero,
                error => console.warn(error)
            );

        this.routeParamsSubscription = this.route
            .params
            .subscribe(
                params => this.routeParamsObserver.next( +params["id"] ),
                error => console.warn(error)
            );
    }

    ngOnDestroy() {
        this.routeParamsSubscription.unsubscribe();
        this.heroSubscription.unsubscribe();
        this.routeParamsObserver.complete();
    }
}

Here is my HeroService in hero.service.ts:

import { Injectable } from "@angular/core";
import { Http, Response } from "@angular/http";
import { Observable } from "rxjs/Observable";
import { Observer } from "rxjs/Observer";
import "rxjs/add/operator/merge";
import "rxjs/add/operator/map";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/catch";

import { Hero } from "./hero";

@Injectable()
export class HeroService {
    constructor(private http:Http) {}

    getHeroSubscription(heroIdObservable:Observable<number>):Observable<Hero> {
        let heroObserver:Observer<Hero>;
        let heroObservable = new Observable<Hero>((observer:Observer<Hero>) => {
            heroObserver = observer;
        });

        let heroIdSubscription = heroIdObservable.subscribe(
            heroId => {
                const tmp = this.http
                    .get("api/heroes/" + heroId)
                    .map((response:Response) => response.json().data)
                    .catch((response:any) => {
                        if (response instanceof Response) {
                            return Observable.throw(response.json().error);
                        }
                        return Observable.throw(response);
                    });
                heroObservable.merge(tmp);
            },
            error => console.warn(error),
            () => {
                heroIdSubscription.unsubscribe();
                heroObserver.complete();
            }
        );

        return heroObservable;
    }
}

And here is my Hero in hero.ts:

export class Hero {public id: number;}

Where is my mistake?

user2112300
  • 244
  • 3
  • 9
  • 1
    Why are you subscribing to a route parameter? I'm not sure I understood your purpose here. – Fabricio Jul 21 '16 at 13:03
  • Becase I bind `HeroViewerComponent` to a route `heroes/:id` so that the parameter can be changed without destroying the component. For example, I am on the route `heroes/10043` and `HeroViewerComponent` renderers the hero with ID `10043`. When I move on (ie on the route `heroes/874`), the component just renderers a new hero. – user2112300 Jul 21 '16 at 13:17

1 Answers1

1

I guess, the possible problem, that heroObservable.merge(tmp) doesn't mutate heroObservable, but returns new observable, which is lost in your code.

Try to store it in any variable and return for getHeroSubscription method instead of just return heroObservable

Oleg Barinov
  • 1,635
  • 12
  • 7
  • Thanks, it seems to solve the problem. Is there a way to mutate `heroObservable` ("put" one `Observable` inside the other)? – user2112300 Jul 21 '16 at 13:22
  • 1
    Only using static methods from Observable, like `zip` or `forkJoin` - you can try to find smth in examples, like https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/forkjoin.md – Oleg Barinov Jul 21 '16 at 13:38