Angular12/rxjs: 更新模板中的可观察数据并显示模板中的变化

Angular12/rxjs: updating observable data in template and showing changes in template

所以我正在进行实时搜索,根据用户输入获取数据。

示例:

export class MyComponent implements OnInit {
      constructor(private httpHandler: HttpHandlerService) { }

      defaultSearchParams: SearchParams = { search: 'bla' }
      searchParams = new BehaviorSubject<SearchParams>(this.defaultSearchParams);
      myData$?: Observable<PageObjects<MyDataObject>>;
    
      ngOnInit(): void {
        this.myData$ = this.searchParams.pipe(
          debounceTime(300),
          switchMap((searchParams: SearchParams) => this.fetchMyData(searchParams)),
        );
      }
    
      fetchMyData(searchParams: SearchParams): Observable<MyDataObject> {
        return this.httpHandler.getMyData(searchParams);
      }
}

我的模板显示数据:

  <div *ngIf="(myData$ | async) as resultset$">
      <div *ngIf="resultset$.elements && resultset$.elements.length">
          <div *ngFor="let dataObject of resultset$.elements">
                ID: {{dataObject.id}}, name {{dataObject.name}}, status {{dataObject.status}}
          </div>
      </div>
  </div>

好吧,挑战来了: 在我的模板中,我希望能够更改数据(即新名称、新状态)。

在使用像我这样的可观察对象时,如何在模板中反映这些更改的数据?

所需结果的示例:


  <div *ngIf="(myData$ | async) as resultset$">
      <div *ngIf="resultset$.elements && resultset$.elements.length">
          <div *ngFor="let dataObject of resultset$.elements">
                ID: {{dataObject.id}}, name {{dataObject.name}}, status {{dataObject.status}}
                <button (click)="changeStatus(dataObject)" type="button">Change status</button>
          </div>
      </div>
  </div>
    changeStatus(MyDataObject dataObject) {
         this.httpHandler.changeStatusOfObject(dataObject, 'newStatus')
                .subscribe(newDataObject => //WHERE should I put the newDataObject to, to show the new value in the template???);
    }

尝试更改changeStatus函数,将更改推送到预定义的BehaviorSubjectchanges$,它与searchParams observable结合形成myData$ observable。

您可以尝试以下方法:

  changes$ = new BehaviorSubject<MyDataObject[]>([]);

  ngOnInit(): void {
    this.myData$ = combineLatest([
      this.searchParams.pipe(
        debounceTime(300),
        switchMap((searchParams: SearchParams) =>
          this.fetchMyData(searchParams)
        )
      ),
      this.changes$,
    ]).pipe(
      map(([data, changes]) => {
        if (changes?.length && data?.elements) {
          changes.forEach((changedItem) => {
            const ndx = data.elements.findIndex(
              (item) => item.id === changedItem.id
            );
            if (ndx !== -1) data.elements[ndx] = changedItem;
          });
        }
        return data;
      })
    );
  }
  changeStatus(dataObject: MyDataObject) {
    this.httpHandler
      .changeStatusOfObject(dataObject, 'newStatus')
      // take one value from this observable and then complete it, to avoid memory leak
      .pipe(take(1))
      .subscribe((newDataObject) => {
        // push the newDataObject to the chagnes$ subject.
        this.changes$.next([newDataObject]);
      });
  }

在我看来,您可以使用 BehaviorSubject 来流式传输数据,并在需要更新单个项目时对其进行调整(无需从后端请求数据)。我会这样做:

export class MyComponent implements OnInit {
      constructor(private httpHandler: HttpHandlerService) { }

      defaultSearchParams: SearchParams = { search: 'bla' }
      searchParams = new BehaviorSubject<SearchParams>(this.defaultSearchParams);
      myData$ = new BehaviorSubject<PageObjects<MyDataObject> | null>(null);
    
      ngOnInit(): void {
        this.searchParams.pipe(
          debounceTime(300),
          switchMap((searchParams: SearchParams) => this.fetchMyData(searchParams)),
        ).subscribe(pagedData => myData$.next(pagedData));
      }
    
      fetchMyData(searchParams: SearchParams): Observable<MyDataObject> {
        return this.httpHandler.getMyData(searchParams);
      }

      changeStatus(dataObject: MyDataObject) {
        this.httpHandler
          .changeStatusOfObject(dataObject, 'newStatus')
          .subscribe((newDataObject) => {
            // because now we have a behavior subject, we can get the last emitted value and use it to change just the updated object
            const pagedData = this.myData$.value;
            const elements = [...pagedData.elements];
            const updatedElementIndex = elements.findIndex(e => e.id === newDataObject.id);
            elements[updatedElementIndex] = newDataObject;
            this.myData$.next({
              ...pagedData,
              elements
            });
          });
      }
}