正确使用flatMap-将使mergeMap或concatMap更有效

时间:2019-03-13 07:01:18

标签: angular rxjs

正如我过去讨论的那样,我是编码和尝试自学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?)来执行此操作。任何建议和想法将不胜感激。如果我没有任何意义或措辞不好,请注明,然后我将进行重写/编辑。预先非常感谢。

1 个答案:

答案 0 :(得分:0)

实际上,您应该使用switchMap而不是concatMapflatMap,即使源输入有新值,flatMap也会保持内部可观察的运行并发出值。 ,如果您使用键盘输入。因此,用户最终在屏幕上看到的内容将闪烁他的最后输入结果+最新结果。

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