首页 > 解决方案 > 如何从服务中封装的流中获取已检查的用户?

问题描述

有一个服务UsersService包含用户逻辑:

interaface User {
    id: number,
    name: string,
    checked?: boolean;
}

class UserService {
   public users$ = new BehaviorSubject<User[]>([]);
   public load() {
       this.http.get('users').subscribe((users: Users[] => this.users$.next(users));
   }
   
   public get users() {
       return this.users$.asObservable();
   }
}

我从组件内部的服务中获取数据:

public users$: Observable<User[]>;
constructor(private userService: UserService) {
    this.users$ = this.userService.users;
}

模板是:

<div> *ngFor="let user of users$ | async">
   <div>{{ user.name }}</div>
   <div><mat-checkbox [(ngModel)]="user.checked"></mat-checkbox></div>
</div>
<div>Checked users: {{ checkedusers | json }}</div>

如何{{ checkedusers | json }}在更新一个复选框或所有复选框后使用流中的 rxjs 方法获取所有已检查用户并将其封装到服务中?

标签: angularrxjs

解决方案


像这样的东西似乎有效:

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 },
];

这是更新的链接:https ://stackblitz.com/edit/angular7-rxjs-ha6suw

  • 当用户的检查更改时,主题会发出用户。
  • allUsers$需要更改以调用您的 http 端点
  • 当检查或未选中复选框时,从组件调用了更新器。它使用适当的检查标志创建一个新的用户对象并将其发送到主题中。
  • checkedUsers$原始用户集与每个发出的更改用户合并。然后它scan用于保留用户数组。当发出更新的用户时,它会在数组中找到该用户并替换它。然后它将结果数组映射为仅显示那些被选中的数组。

更新:根据 OP,添加了“全选”和“取消全选”功能。

@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 }));
    }
  }
}

这是更新的链接:https ://stackblitz.com/edit/angular7-rxjs-ahp158


推荐阅读