Something like this seemed to work:
import { Injectable } from '@angular/core';
import { merge, of, Subject } from 'rxjs';
import { map, scan } from 'rxjs/operators';
export interface User {
id: number;
name: string;
checked?: boolean;
}
@Injectable({ providedIn: 'root' })
export class UserService {
readonly userChangeSubject = new Subject<User>();
userChanged$ = this.userChangeSubject.asObservable();
allUsers$ = of(userData);
updateUser(user: User, checked: boolean) {
const updatedUser = { ...user, checked: checked };
this.userChangeSubject.next(updatedUser);
}
checkedUsers$ = merge(
this.allUsers$,
this.userChanged$
).pipe(
scan((users: User[], user: User) => users.map((u) => u.id === user.id ? user : u)),
map(users => users.filter((u) => u.checked))
);
}
export const userData: User[] = [
{ id: 1, name: 'Captain Jack', checked: false },
{ id: 2, name: 'Mal Reynolds', checked: false },
{ id: 3, name: 'NaomiNagata', checked: false },
];
Here is the updated link: https://stackblitz.com/edit/angular7-rxjs-ha6suw
- The Subject emits a user when the user's check is changed.
- The
allUsers$ will need to be changed to call your http endpoint
- The updateUser is called from the component when a checkbox is checked or unchecked. It creates a new user object with the appropriate check flag and emits it into the Subject.
- The
checkedUsers$ merges the original set of users with each emitted changed user. It then uses scan to retain the array of users. When an updated user is emitted, it locates that user in the array and replaces it. It then maps the resulting array to only display those that are checked.
UPDATE: As per the OP, added "check all" and "uncheck all" functionality.
@Injectable({ providedIn: 'root' })
export class UserService {
// User or null for all users
// Defined as a tuple with the user and checked flag
readonly userCheckedSubject = new Subject<[User | null, boolean]>();
userChecked$ = this.userCheckedSubject.asObservable();
// This would be an http.get to get the users
retrievedUsers$ = of(userData);
allUsers$ = merge(this.retrievedUsers$, this.userChecked$).pipe(
// Use array destructuring to access the tuple values
scan((users: User[], [changedUser, checked]: [User | null, boolean]) =>
this.processUsers(users, changedUser, checked)
),
shareReplay(1)
);
checkedUsers$ = this.allUsers$.pipe(
map((users) => users.filter((u) => u.checked)),
shareReplay(1)
);
updateUser(user: User | null, checked: boolean) {
this.userCheckedSubject.next([user, checked]);
}
processUsers(
users: User[],
changedUser: User | null,
checked: boolean
): User[] {
if (changedUser) {
// Update the one changed item
const updatedUser = { ...changedUser, checked: checked };
return users.map((user) =>
user.id === updatedUser.id ? updatedUser : user
);
} else {
// Update each item
return users.map((user) => ({ ...user, checked: checked }));
}
}
}
Here is the updated link: https://stackblitz.com/edit/angular7-rxjs-ahp158