使用 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'。
可以将初始值提供给带有startWith运算符的可观察对象,正如现在已删除的答案中已经提到的那样。
问题是filterLocation$分配太晚了,之后filterLocation$ | async被评估为null. 由于更改发生在同一滴答上,这会导致更改检测错误(但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)
)
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)
| 归档时间: |
|
| 查看次数: |
13222 次 |
| 最近记录: |