【问题标题】:Correct use of flatMap - would mergeMap or concatMap be more efficienient正确使用 flatMap - mergeMap 或 concatMap 会更高效
【发布时间】:2019-03-13 07:01:17
【问题描述】:

正如我过去所讨论的,我是编码新手,并试图自学 Angular - 也许这会带来新的职业!我正在为自己创建一个应用程序来掌握 Web 开发。我目前正在尝试在表单中使用材料自动完成功能,以便当用户向文本输入添加/插入值时,keyup 事件也会在一个可观察的对象中被监听,该可观察对象进行返回一些数据的 API 调用,我使用返回的日期填充自动完成(我的页面中有 3 个)。这是我的 HTML 表单...

<form novalidate [formGroup]="assignmentForm">
      <div>
        <input type="text" matInput placeholder="User" formControlName="worker" name="worker" [matAutocomplete]="workerTemplate" #worker>
        <mat-autocomplete #workerTemplate="matAutocomplete">
          <mat-option *ngFor="let worker of workerTags" [value]="worker">{{ worker.displayName}}</mat-option>
        </mat-autocomplete>
      </div>

      <div>
        <input type="text" matInput placeholder="Company" formControlName="company" name="company" [matAutocomplete]="companyTemplate" #company>
        <mat-autocomplete #companyTemplate="matAutocomplete">
          <mat-option *ngFor="let company of companyTags" [value]="company">{{company.displayName}}</mat-option>
        </mat-autocomplete>
      </div>

      <div>
        <input type="text" matInput placeholder="Department" formControlName="department" name="department" [matAutocomplete]="departmentTemplate" #department>
        <mat-autocomplete #departmentTemplate="matAutocomplete">
          <mat-option *ngFor="let department of departmentTags" [value]="department">{{department.displayName}}</mat-option>
        </mat-autocomplete>
      </div>
    </form>

现在在我的组件中,我使用 Observable.merge 来监听所有三个输入,我去抖动以便用户不会使系统过载,我调用我的 API,然后在填充相关数据数组之前执行一些格式化逻辑自动完成。这是我的组件代码(为了便于阅读,我已经减少了这个)

public companyTags: any[];
public departmentTags: any[];
public workerTags: any[];

@ViewChild('company')
private companyEl: ElementRef;
@ViewChild('department')
private departmentEl: ElementRef;
@ViewChild('worker')
private workerEl: ElementRef;
private assignmentSubscription: Subscription;

constructor(private apiService: ApiService) {}

public ngOnInit() {
  const companySource = fromEvent(this.companyEl.nativeElement, 'keyup');
  const departmentSource = fromEvent(this.departmentEl.nativeElement, 'keyup');
  const workerSource = fromEvent(this.workerEl.nativeElement, 'keyup');

  const tagsSource = merge(companySource, departmentSource, workerSource)
    .pipe(
      debounceTime(500),
      distinctUntilChanged(),
      flatMap((ev: KeyboardEvent) => {
        // if the user presses backspace the value is "" and all results are returned (to set limit)
        if ((<HTMLInputElement>ev.target).value !== '') {
          return this.apiService.getTags((<HTMLInputElement>ev.target).name, (<HTMLInputElement>ev.target).value, 3)
        }

        return of([]);
      }),
    );

  this.assignmentSubscription = tagsSource.subscribe((res) => {
    this.clearAllTags();
    if (res.length > 0) {
      // the type is contained in the array so we can determine which array we need to populate
      // we can use interpolation rather than a horrible if then else
      this[`${res[0].type}Tags`] = res;
    }
  });
}

public clearAllTags(): void {
  this.companyTags = null;
  this.departmentTags = null;
  this.workerTags = null;
}

这一切都会奏效,但我想知道这是否是最有效的方法?我已经阅读了 flatMap、mergeMaop 和 concatMap,但我不确定哪种方法最适合我的情况?我是否应该将 flatMap 中包含的逻辑放在其他地方,因为它似乎是错误的地方?我不确定我什至会如何使用链接或向管道添加另一种方法来做到这一点(.do?)。任何建议和想法将不胜感激。如果我没有任何意义或我的问题措辞很糟糕,请说明,我将重写/编辑。非常感谢。

【问题讨论】:

    标签: angular rxjs


    【解决方案1】:

    事实上,您应该使用switchMap 而不是concatMapflatMap,即使源输入(在您的情况下为键盘输入)输入了新值,flatMap 也会保持内部可观察对象运行并发出值.所以用户最终在屏幕上看到的将是他最后的输入结果+最新的结果。

    例如,当您尝试搜索“apple”时,您会从源输入中获取“app”,然后 api 将搜索“app”,但在 api 返回之前,您输入了“le”,现在应该返回结果'苹果'。如果在这种情况下使用 flatMap,它将首先显示 'app' 的结果,然后快速更改为 'apple'。而switchMap 被使用,'app' api 搜索请求将被跳过(不是真正取消但不发出值)并返回 'apple' 搜索。

    【讨论】:

      猜你喜欢
      • 2018-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      • 2019-05-21
      • 2017-04-16
      相关资源
      最近更新 更多