2

I have a component in which I am displaying a table of data (users) generated from an http request to an API. There are a variety of filters on the table so I generate the table data as follows:

this.displayUsers$ = Observable.combineLatest(
            this.users$,
            this.siteFilter$,
            this.clientFilter$,
            this.activeFilter$,
            this.nameFilter$
        )
            .map(([users, siteFilter, clientFilter, activeFilter, nameFilter]) => {
                console.log("Users changed? ", users);
                if (siteFilter === null
                    && clientFilter === null
                    && activeFilter === null
                    && nameFilter.length < 1) {
                    return users;
                }
                return users
                    .filter(user => {
                        // manipulate users array based on filters
                    })
            });

The xFilter$s are BehaviorSubjects that emit new values in response to changes to their inputs in the template. users$ is assigned as follows:

this.users$ = this.userService.getList();

Where getList() returns an http request to get a list of users.

The list successfully updates whenever the filters are changed, but I have issues when I need to update the list with fresh users. For example, a user can perform CRUD operations on the table of users (ie delete a user). When the user is deleted, however, the table does not update to reflect this change. The method I am currently using to (try) to fresh the table is by calling a refresh function in the success callback of the delete operation:

refreshUserList() {
        console.log("Refreshing user list");
        this.users$ = this.userService.getList();
    }

Despite calling this after the operation is complete, the combineLatest observable does not get 'triggered' again and the list data remains stale. Is there a better way to refresh data generated by combineLatest with http requests?

natmegs
  • 121
  • 2
  • 16

2 Answers2

3

The main problem is that in the case of user list update your users$ observable does not emit any new value. You have to make it do so.

This is skeleton of an idea how to fix it:

const updateUsersTrigger = new Subject<void>();

this.displayUsers$ = Observable.combineLatest(
    // switchMap() will re-subscribe to this.userService.getList() observable on each emit from updateUsersTrigger
    // thus making http requests each time when user list should be updated.
    updateUsersTrigger.startWith(null).switchMap(() => this.userService.getList()),
    this.siteFilter$,
    this.clientFilter$,
    this.activeFilter$,
    this.nameFilter$
)
    .map(([users, siteFilter, clientFilter, activeFilter, nameFilter]) => {

    .......

});

// this will trigger user list update
updateUsersTrigger.next();
Alexander Leonov
  • 4,694
  • 1
  • 17
  • 25
  • This makes a lot of sense, thanks for the suggestion. I think I'm missing something with my implementation, however, because now I'm getting no table data (even when I change filters). I changed refreshUserList() { updateUsersTrigger.next(); } and call this in ngOnInit() but still no luck. – natmegs Oct 10 '17 at 19:49
  • @natmegs, try something like this: `updateUsersTrigger.startWith(null).switchMap(() => ... call service ...)` instead of emitting something in ngOnInit() - I wouldn't expect it to work actually, I think it fires too early. – Alexander Leonov Oct 10 '17 at 19:55
1

I am not able to comment yet, so I'll write here and correct me if I'm wrong or if this is out of the question. If you use .splice() on successful delete to update the DOM instead of sending another http request, wouldn't that be easier and less straining on the client?

  • Either way OP needs some observable to emit new array, be it http request or splice() result in some Observable.map() or something else. – Alexander Leonov Oct 10 '17 at 19:37