简体   繁体   中英

Angular Observable subscription fallback?

I'm using Angular 4 and I have a component that lives at the 'player/:id' route in my application. When the player navigates to this route from within the application I use a currentPlayerService to sync the current player within the app. This works great with the below code.

this.currentPlayerService.getPlayer()
  .subscribe((player) => this.currentPlayer = player);

However, when the application loads directly to the 'player/:id' route (from an external link) I can comment out the above and use the code below to set the currentPlayer from the route params.

this.route.params.subscribe(params => {
  this.playerService.getPlayer(params.id)
    .subscribe((player) => {
      this.currentPlayer = player;
      this.currentPlayerService.setPlayer(player);
    });
 });

What I'd like to do (and am having difficulty finding the "reactive function programming" way of saying) is to load the player from the params only if this.currentPlayerService.getPlayer() doesn't have a value on load. I'm having a hard time conceiving of a way to use the Observable in some kind of logic control flow, so any help would be appreciated.

Thanks!

Observables itself is stateless. To keep track of a value (or current value), you will need to use either Subject or BehaviorSubject .

In your currentPlayerService , create a BehaviorSubject called playerBSubject :

export class CurrentPlayerService{
  public playerBSubject: BehaviorSubject<any>;//can be type of Player if you have such type
  constructor(){
    this.playerBSubject = new BehaviorSubject({})//any value that you want to initialize
  }

  getPlayer(){
    //do your logic here, whatever that is.
    //I am using an Observable.of to mimic a http request
    Observable.of({})
      .subscribe((player)=>{
      this.playerBSubject.next(player)//this will update the subject with the latest value
    });
    return this.playerBSubject.asObservable();
  }

}

Note that the .next() method will update the values of your subject, and you can return it as an observable using .asObservable() .

Now, in your component controller, you can check if your BehaviourSubject exist (or has the value you want), and only call playerService.getPlayer() if need to.

this.route.params.subscribe(params => {
    //check if BehaviorSubject exist or not
    if (this.currentPlayerService.playerBSubject.getValue() === {}) {
        this.playerService.getPlayer(params.id)
            .subscribe((player) => {
                this.currentPlayer = player;
                this.currentPlayerService.setPlayer(player);
            });
    }
});

Suggestions:

I am not sure why you need two service namely currentPlayerService and playerService . If your currentPlayerService is just to keep track of "current" value of the player, then you do not need it at all, should you use BehaviorSubject to keep track of your current player. All of them can boil down into one single service.

export class PlayerService {
    public playerBSubject: BehaviorSubject<any>;

    constructor() {
        this.playerBSubject = new BehaviorSubject({})
    }

    getPlayer(id) {
        Observable.of({id}) // implement your own logic
            .subscribe((player) => {
                this.playerBSubject.next(player)
            });
        return this.playerBSubject.asObservable();
    }

    setPlayer(id) {
        return Observable.of({id})//implement your own logic
            .subscribe(newPlayer => this.playerBSubject.next(newPlayer))
    }
}

And in your controller if you want to get the current value you can just do:

this.currentPlayer = this.playerService.playerBSubject.getValue();

And with a little help of asObservable you can do this:

this.playerService
    .asObservable()
    .subscribe(player => {
        if (player === {}) {
            this.route.params.subscribe(params => {
                this.playerService.getPlayer(params.id); //voila, your player is updated
            })
        }
        //remember to update the value
        this.currentPlayer = player
    })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM