Skip to content

Instantly share code, notes, and snippets.

@anshdivu
Created April 20, 2016 19:05
Show Gist options
  • Save anshdivu/9242342907ef4f709bcaed19861da3b0 to your computer and use it in GitHub Desktop.
Save anshdivu/9242342907ef4f709bcaed19861da3b0 to your computer and use it in GitHub Desktop.
`$scope.$watch` to an Observer
References:
  • RxJS api
  • ReactiveX - main website which explains what are observables/observers.
  • RxMarbles - super cool website which visually explains all the observable operators/api functions.
$scope.$watch to rxjs.observer:

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:

  1. watching takersMarket changes
  2. check if new takersMarket has a bidSuggestion
  3. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment