可观察到的初始值

edk*_*ked 7 angular

使用 observable,我想过滤并显示一个列表。输入event只有当用户开始输入被触发。因此,列表不会首先显示。this.filterLocation$inputEvent开始被触发之前,如何为 observable 分配默认值?

模板

<ng-template ngFor let-location [ngForOf]="filterLocation$ | async">
        <a mat-list-item href="#">{{location}}</a>
      </ng-template>
Run Code Online (Sandbox Code Playgroud)

成分

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)
  )
 }
}
Run Code Online (Sandbox Code Playgroud)

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

错误:ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。以前的值:'ngForOf: null'。当前值:'ngForOf: name1,name2'。

实时代码

Est*_*ask 7

可以将初始值提供给带有startWith运算符的可观察对象,正如现在已删除的答案中已经提到的那样。

问题是filterLocation$分配太晚了,之后filterLocation$ | async被评估为null. 由于更改发生在同一滴答上,这会导致更改检测错误(但ExpressionChangedAfterItHasBeenCheckedError如果预期会发生,则可以将其视为警告)。

解决方案是在触发更改检测之前将代码从 移动ngAfterViewInitngOnInit

这并不总是可能的。另一种方法是异步提供一个值,这样它就不会干扰初始更改检测。

通过使用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)
  )
Run Code Online (Sandbox Code Playgroud)

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

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)
  )
Run Code Online (Sandbox Code Playgroud)