具有初始值

时间:2018-05-28 22:10:04

标签: angular

使用observable,我想过滤并显示一个列表。仅当用户开始键入时才会触发输入event。因此,列表不会显示在第一位。在this.filterLocation$开始触发之前,如何为可观察的inputEvent分配默认值?

模板

<ng-template ngFor let-location [ngForOf]="filterLocation$ | async">
        <a mat-list-item href="#">{{location}}</a>
      </ng-template>

组件

ngAfterViewInit() {
const searchBox = document.querySelector('#search-input');
this.filterLocation$ = fromEvent(searchBox, 'input')
  .pipe(
    map((e: any) => {
      const value = e.target.value;
        return value ? this.locations
          .filter(l => l.toLowerCase().includes(value.toLowerCase()))
          : this.locations;
      }),
      startWith(this.locations)
  )
 }
}

使用startWith可以使列表最初显示。但是抛出了以下错误:

  

错误:ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。上一个值:&#39; ngForOf:null&#39;。当前值:&#39; ngForOf:name1,name2&#39;。

live code

1 个答案:

答案 0 :(得分:3)

可以使用spring.batch.job.enabled=false 运算符向具有startWith运算符的observable提供初始值,因为现在已删除的答案中已经提到过。

问题是在filterLocation$评估为filterLocation$ | async后,null分配得太晚了。由于更改发生在相同的tick上,因此会导致更改检测错误(尽管ExpressionChangedAfterItHasBeenCheckedError可能会被视为警告,如果它出现的话)。

解决方案是在触发更改检测之前将代码从ngAfterViewInit移至ngOnInit

这并不总是可行的。另一种方法是异步提供一个值,因此它不会干扰初始变化检测。

通过delay运算符延迟整个observable(用户输入的可接受解决方案,因为它不是时间关键):

  this.filterLocation$ = fromEvent(searchBox, 'input')
  .pipe(
    map((e: any) => { 
      const value = e.target.value;
        return value ? this.locations
          .filter(l => l.toLowerCase().includes(value.toLowerCase()))
          : this.locations;
    }),
    startWith(this.locations),
    delay(0)
  )

或者使初始值与调度程序异步:

import { asyncScheduler } from 'rxjs'
...

  this.filterLocation$ = fromEvent(searchBox, 'input')
  .pipe(
    map((e: any) => { 
      const value = e.target.value;
        return value ? this.locations
          .filter(l => l.toLowerCase().includes(value.toLowerCase()))
          : this.locations;
    }),
    startWith(this.locations, asyncScheduler)
  )