- RxJS api
- ReactiveX - main website which explains what are observables/observers.
- RxMarbles - super cool website which visually explains all the observable operators/api functions.
Angular $watch
and rxjs observers are basically the same thing. They just have different semantics. This is why angular 2 will completely remove $watch
and directly use rxjs
.
So $watch
is function that returns data every time some event occurs and in this case the event is when the object being watched changes. Rxjs
also returns data every time an event occurs. Promises do the same thing. The only difference between rxjs and promise is that promise is completed after one event occurs. Whereas, rxjs
/$watch
can keep watching for more events to occur. Another advantage of rxjs over $watch
and promises is that it has built in lodash
style functions that can be run on the data being returned. This is where most of the power of rxjs comes from.
Ok, so lets look at this example:
If I was writing this with plain raw $watch
this is how it will look like
// watch if takersMarketGridRowCtrl.takersMarket changes
$scope.$watch('takersMarketGridRowCtrl.takersMarket', function(newTakersMarket, oldTakersMarket) {
var takersMarket = newTakersMarket;
if (takersMarket && takersMarket.bidSuggestion) { // check if this takersMarket has a bid suggestion
// if bid suggestions exist trigger low offer popover
vm.showLowOfferPopover = !app.storage.seenLowOfferWarning;
app.storage.seenLowOfferWarning = true;
}
}, true);
This $watch
function is doing 3 things:
- watching
takersMarket
changes - check if new
takersMarket
has abidSuggestion
- triggers low offer popover if bid value is being suggested
Lets break out 3 steps into private function so that we don't have write all that messy code in the activate
function:
function _bidSuggestionWatcher(callBackFn) {
$scope.$watch('takersMarketGridRowCtrl.takersMarket', function(newTakersMarket, oldTakersMarket) {
var takersMarket = newTakersMarket;
if (takersMarket && takersMarket.bidSuggestion) {
callBackFn(takersMarket.bidSuggestion);
}
}, true);
}
function activate() {
_bidSuggestionWatcher(function(bidSuggestion) {
vm.showLowOfferPopover = !app.storage.seenLowOfferWarning;
app.storage.seenLowOfferWarning = true;
})
}
Instead of doing the trick with passing around a callback we can convert the $watch
to an observer
. Think of it in the same we convert functions with callbacks into promises. An observer
is just a promise that can handle more than one events. Now lets convert _bidSuggestionWatcher
to _bidSuggestionObserver
and remove the need for a callback.
function _bidSuggestionObserver() {
return $scope.$toObservable('takersMarketGridRowCtrl.takersMarket', true).map(function(values) { // `values` format => {newValue: obj, oldValue: obj}
var takersMarket = values.newValue;
return takersMarket.bidSuggestion; // return only the bidSuggestion since this is a bidSuggestionObserver
});
}
function activate() {
_bidSuggestionObserver().subscribe(function(bidSuggestion) { // `subscribe` is the equivalent to `then` in a promise; it executes the `observer` and return the data
vm.showLowOfferPopover = !app.storage.seenLowOfferWarning;
app.storage.seenLowOfferWarning = true;
});
}
Remember in the beginning I said the real power of rxjs
comes from it's lodash
style operators. In this case, we are using map
to convert takersMarket
to bidSuggestion
. If you look at the callback example it was only returning bidSuggestion
back to the callback. That effect is achieved by map
here.
The only thing missing here from the call back example is the if
condition that only calls the callback if bidSuggestion
is found (we only want to show the popover when there is a bidSuggestion
).
This can be done using a filter
on the observer
. It will filter out all the results where bidSuggestion
is not available.
After adding filter
the following becomes the final version:
function _bidSuggestionObserver() {
return $scope.$toObservable(
'takersMarketGridRowCtrl.takersMarket', true
).map(function(values) { // `values` format => {newValue: obj, oldValue: obj}
var takersMarket = values.newValue;
return takersMarket.bidSuggestion; // return only the bidSuggestion since this is bidSuggestionObserver
}).filter(function(bidSuggestion) {
return bidSuggestion; // return (bidSuggestion !== null && bidSuggestion !== undefined && bidSuggestion !== false);
});
}
function activate() {
_bidSuggestionObserver().subscribe(function(bidSuggestion) { // `subscribe` is the equivalent to `then` in a promise; it executes the `observer` and return the data
vm.showLowOfferPopover = !app.storage.seenLowOfferWarning;
app.storage.seenLowOfferWarning = true;
});
}
Please ask questions if anything is not clear.