Angular 2 - Show loading-information when (observableData | async) is not yet resolved

前端 未结 6 2086
抹茶落季
抹茶落季 2020-12-29 05:13

just as the title says, I want to embrace the power of rxjs Observables.

What I do now:

// dataview.html
Loading data
6条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-29 05:50

    This is my current best attempt for displaying search results.

    I thought about extending Observable somehow to include an isLoading property - or returning a tuple but in the end a helper function (in my service) that returns a pair of observables seems to be the cleanest way. Like you I was looking for some 'magic' but I can't see any better way to do it than this.


    So in this example I have a FormGroup (a standard reactive form) which contains search criteria:

    { email: string, name: string } 
    

    I get the search criteria from the form's valueChanges observable when it changes.

    Component Constructor

    Note: The search isn't actually run until the criteria change, which is why this is in the constructor.

    // get debounced data from search UI
    var customerSearchCriteria = this.searchForm.valueChanges.debounceTime(1000);
    
    // create a pair of observables using a service (data + loading state)
    this.customers = this.customersService.searchCustomers(customerSearchCriteria);
    
    // this.customers.data => an observable containing the search results array
    // this.customers.isLoading => an observable for whether the search is running or not
    

    Search Service

    public searchCustomers(searchCriteria: Observable):
                           { data: Observable, 
                             isLoading: Observable }
    {
        // Observable to track loading state
        var isLoading$ = new BehaviorSubject(false);
    
        // Every time the search criteria changes run the search
        var results$ = searchCriteria
                        .distinctUntilChanged()
                        .switchMap(criteria =>
                        {
                            // update isLoading = true
                            isLoading$.next(true);
    
                            // run search
                            var search$ = this.client.search(new CustomersSearch(criteria)).shareReplay();
    
                            // when search complete set isLoading = false
                            search$.subscribe({ complete: () => isLoading$.next(false) });
    
                            return search$;
                        })
                        .shareReplay();
    
        return { data: results$, isLoading: isLoading$ };
    }
    

    Need to find some way to make this generic, but that's pretty easy. Also note that if you don't care about isLoading you simply do searchCustomers(criteria).data and then you're just getting to the data.

    Edit: needed to add an extra ShareReply to prevent search firing twice.

    Component HTML

    Use both customers.data and customers.isLoading as observables as normal. Remember customers is just an object with two observable properties on it.

    Loading data...
    • {{ d.email }}

    Also note that you need the async pipe for both observables. I realize that looks a little clumsy for the isLoading, I believe that it is faster to use an observable than a property anyway. There could be a refinement to this, but I'm not yet an expert but would certainly welcome improvements.

提交回复
热议问题