sixtyfootersdude
sixtyfootersdude

Reputation: 27241

How to get an observable that changes whenever another observable changes

Problem

I have a service that looks like this:

DogService { 
    public getSelectedDog(): Observable<Uuid> {...}
    public getDog(dogUuid: Uuid): Observable<Dog> {...}
}

I would like to create an Observable that always contains my selected dog, ie Observable<Dog>.


What I have tried so far

Currently what I am doing is this:

let selectedDog: Observable<Dog> =  dogService.getSelectedDog().flatMap( selectedDog => {
    return dogService.getDog( selectedDog );
});

Then you can subscribe to changes in the selected dog like this:

selectedDog.subscribe( dog => console.log( 'dog: ", dog.name, dog.mood );

Unfortunatly, this doesn't seem to work the way I expected it to. The selectedDog observable only fires when the selectedDog has changed by not when the dog itself changes.

So for example lets say that we do the following:

const dog1: Dog = new Dog( '1',      // uuid
                           'Shadow', // name
                           'happy'   // mood
);

const dog2: Dog = new Dog( '2',      // uuid
                           'Barky',  // name
                           'Angry'   // mood
);


// Fires the getDog observable
dogService.addDog( dog1 );  

// Fires the getDog observable
dogService.addDog( dog2 );

// Fires the getSelectedDogObservable
dogService.selectDog( dog1.uuid )

// --- Now in console.log we will see: 'dog: Shadow happy'

Unfortunatly, if I update the dog, the console doesn't get logged to:

const updatedDog1: Dog = new Dog( '1',      // uuid
                                  'Shadow', // name
                                  'SAD'   // mood
                                   );
// Fires the getDog observable
dogService.updateDog( updatedDog1 );

// --- the console.log doesn't get invoked.  Crap.

Question: How can I create Observable<Dog> that will fire whenever the selection or selected dog changes?

Upvotes: 0

Views: 393

Answers (1)

Lodewijk Bogaards
Lodewijk Bogaards

Reputation: 20009

Your code should work, so I suspect a problem in your service. I have some code to demonstrate that in principle it should work.

One mistake you should correct though is that you are using flatMap, but should be using switchMap. The thing is that flatMap will merge every stream into your stream regardless of when you select a new dog, meaning you will keep getting updates of the previous selected dogs, which you don't want. So here is the code I was talking about:

var dogs = [
  Rx.Observable.timer(0, 500).map(() => 'barky'),
  Rx.Observable.timer(0, 1000).map(() => 'cuddles')
];

var selectedDog = 0;
var dogSelector = Rx.Observable
  .timer(0, 5000)
  .map(_ => {
    selectedDog = (selectedDog ? 0 : 1);
    return selectedDog;
  })
  .switchMap(i => dogs[i]);

dogSelector.subscribe(dog => console.log(`Doggy: ${dog}`))

http://jsbin.com/haqutehajo/1/edit?js,console

A new dog is selected every 5 second and each dog sends an update every 1 or half a second respectively. Also try flatMap instead of switchMap and you will see what I am talking about.

Upvotes: 1

Related Questions