RxJS - Loading indicator
Asked Answered
P

1

8

I'm struggling to get my head around the "Rx" way of displaying a loading indicator for an AJAX stream.

$scope.$createObservableFunction("load")
        .take(1)
        .do(function(){
            $scope.loading = true;
        })
        .flatMap(contentService.AJAX_THINGY_AS_OBSERVABLE)
        .delay(300)
        .subscribe(function(content){
            console.log("content",content);
        },function(error){
            $scope.error = error
        },function() {
            $scope.loading = false;
        });

As far as I understand it I should use .do() for side effects, which I suppose setting loading is, but it doesn't feel like the right way of doing things.

Can anyone provide a cleaner/better/proper example of how to do this?

Thanks!

UPDATE 1

I decided to split this into 2 streams; requestSource and responseSource.

var loadRequestSource = $scope.$createObservableFunction("load")
    .share();

var loadResponseSource = loadRequestSource
    .flatMap(contentService.AJAX_THINGY_AS_OBSERVABLE)
    .throttle(1000)
    .share();

Then have 2 separate subscribers:

loadRequestSource.subscribe(function () {
    $scope.loading = true;
});

loadResponseSource.subscribe(function (response) {
    /* enter logic */
    $scope.loading = false;
    $scope.$digest();
}, function (err) {
    $scope.error = err;
    $scope.loading = false;
    $scope.$digest();
});

I'm liking this approach as it keeps the role of the subscribes accurate. The response subscriber doesn't need to care about setting loading to true. It only cares about setting it to false.

Penetrant answered 13/4, 2015 at 13:45 Comment(3)
lack of $scope.$digest() is not an issue in this case?Icarian
I later found out I needed it, yeah.Penetrant
Anyway, you were asking about the concept, what is "the way" to do it. To be honest, I don't see more appropriate approach :P I would make it testable, ex. decouple logic from code that sets flags on $scope. But beside that, I don't know what you can do better.Icarian
F
2

I like transforming the request/response streams into a single stream that represents the current state of your loading property:

const startLoading$ = loadRequestSource.map(() => true);
const stopLoading$ = loadResponseSource.map(() => false);
const loadingState$ = Rx.Observable.merge(startLoading$, stopLoading$);

// Finally, subscribe to the loadingState$ observable
loadingState$.subscribe(state => {
    $scope.$applyAsync(() => $scope.loading = state);
});
Frostwork answered 1/3, 2016 at 15:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.