代码之家  ›  专栏  ›  技术社区  ›  edkeveked

初始值可观测

  •  3
  • edkeveked  · 技术社区  · 7 年前

    使用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 使列表最初显示。但会引发以下错误:

    错误:ExpressionChangedAfterithasbeencheckeder错误:表达式在检查后已更改。上一个值:“ngforof:null”。当前值:“ngforof:name1,name2”。

    live code

    1 回复  |  直到 7 年前
        1
  •  3
  •   edkeveked    7 年前

    初始值可提供给 startWith 接线员,正如现在删除的答案中所提到的。

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

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

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

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

      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)
      )
    
    推荐文章