首页 > 解决方案 > 如何为(单击)Angular 应用 switchMap?

问题描述

我有以下元素点击:

<div *ngFor="let doc in docs">
<app-checkbox (onSelected)="onSelectedDocument(doc, $event)"                                           [disabled]="isApprepdocsDisabled(doc)"
[checked]="doc.isapprepdocs == 1"
[id]="doc.index"></app-checkbox>
</div>

和事件处理程序:

    public closed$ = new Subject<any>();
    public documentChecked$ = new Subject<any>();

  ngAfterViewInit() {
        this.documentChecked$
            .pipe(
                exhaustMap((data: { docid: number; checked: boolean }) =>
                    iif(
                        () => data.checked,
                        this.applicationDocumentsService.addtoappdoc(data.docid),
                        this.applicationDocumentsService.removefromappdoc(data.docid),
                    ),
                ),
                takeUntil(this.closed$),
            )
            .subscribe(
                (res) => {
                    console.log(res);
                },
                (err) => {},
            );
    }

为什么在服务器错误后下一次单击不发出值?

标签: angularrxjs

解决方案


您可以使用 RxJSiif函数根据条件选择可观察对象。尝试以下

public onSelectedDocument(doc: any, checked: boolean): void {
  iif(
    () => checked,
    this.applicationDocumentsService.addtoappdoc(doc.docid),
    this.applicationDocumentsService.removefromappdoc(doc.docid)
  ).subscribe(
    res => { },
    err => { }
  )
}
### 更新:忽略事件直到当前事件完成###

要忽略传入事件,您需要使用exhaustMap映射运算符。switchMap实际上与您的要求完全相反。当发出新事件时,它将取消当前请求。

您还可以删除(click)模板中的事件绑定并使用ViewChildwith RxJS函数从元素fromEvent中获取click事件。div尝试以下

模板

<div #clickDiv>Click</div>

控制器

import { Component, OnInit, OnInit, AfterViewInit, OnDestroy, ElementRef, ViewChild } from '@angular/core';

import { iif, fromEvent, Subject } from 'rxjs';
import { takeUntil, exhaustMap } from 'rxjs';

export class SomeComponent implements OnInit, AfterViewInit, OnDestroy {
  closed$ = new Subject<any>();    // <-- use to close subscriptions 
  @ViewChild('clickDiv', { static: false }) clickDiv: ElementRef<any>;

  ngAfterViewInit() {
    fromEvent(this.clickDiv.nativeElement, 'click').pipe(
      exhaustMap(_ =>              // <-- ignore incoming events until the current event is complete
        iif(
          () => checked,
          this.applicationDocumentsService.addtoappdoc(doc.docid),
          this.applicationDocumentsService.removefromappdoc(doc.docid)
        )
      ),
      takeUntil(this.closed$)
    ).subscribe(
      res => { },
      err => { }
    );
  }

  ngOnDestroy() {
    this.closed$.next();            // <-- close open subscriptions
  }
}

更新:处理来自多个 DOM 元素的事件

如果您有多个 DOM 元素,请忽略之前的更新。对于多个元素,您实际上需要使用ViewChildren. 您可以继续使用该onSelected事件,但您可以将事件推送到多播可观察对象(如 RxJS Subject),而不是调用事件处理程序。然后可以使用exhaustMap介于两者之间的操作员进行订阅。尝试以下

模板

<div *ngFor="let doc in docs">
  <app-checkbox 
    (onSelected)="docClick$.next(doc)"
    [disabled]="isApprepdocsDisabled(doc)"
    [checked]="doc.isapprepdocs == 1"
    [id]="doc.index"
  ></app-checkbox>
</div>

控制器

import { Component, OnInit, OnInit, OnDestroy } from '@angular/core';

import { iif, Subject, of } from 'rxjs';
import { takeUntil, exhaustMap, catchError } from 'rxjs';

export class SomeComponent implements OnInit, OnDestroy {
  closed$ = new Subject<any>();    // <-- use to close subscriptions
  docClick$ = new Subject<any>();  // <-- event handler for doc click events

  ngOnInit() {
    this.docClick$.asObservable().pipe(
      exhaustMap(doc =>            // <-- ignore incoming events until the current event is complete
        iif(
          () => checked,
          this.applicationDocumentsService.addtoappdoc(doc['docid']),
          this.applicationDocumentsService.removefromappdoc(doc['docid'])
        )
      ),
      catchError(error =>          // <-- return valid response if error so the subscription doesn't error out
        of(error)
      ),
      takeUntil(this.closed$)
    ).subscribe(
      res => { },
      err => { }
    );
  }

  ngOnDestroy() {
    this.closed$.next();            // <-- close open subscriptions
  }
}

推荐阅读