Angular Observable.startWith导致绑定检查错误

时间:2018-01-24 05:20:46

标签: angular rxjs

我正在为Angular中的HTML5音频播放器构建自定义控件,我正在通过以下事件创建一个Observable:

this.$trackLength = Observable.fromEvent(this.audioPlayer.nativeElement, 'canplaythrough')

然后我添加一个.map来获取我想要的值,这非常有用。但我想用{0}初始化Observable,以便它在组件加载时不会显示为空白或null。所以我发现startWith就是这样做的。所以最后,Observable看起来像这样:

this.$trackLength = Observable.fromEvent(this.audioPlayer.nativeElement, 'canplaythrough')
    .map(() => Math.ceil(this.audioPlayer.nativeElement.duration))
    .startWith(0);

看起来应该可行,但确实如此,但我在控制台中收到错误:

ERROR TypeError: Cannot read property 'name' of undefined
    at checkBindingNoChanges (core.js:9912)
    at checkNoChangesNodeInline (core.js:13961)
    at checkNoChangesNode (core.js:13935)
    at debugCheckNoChangesNode (core.js:14764)
    at debugCheckRenderNodeFn (core.js:14704)
    at Object.eval [as updateRenderer] (AudioPlayerComponent.html:21)
    at Object.debugUpdateRenderer [as updateRenderer] (core.js:14686)
    at checkNoChangesView (core.js:13775)
    at callViewAction (core.js:14126)
    at execComponentViewsAction (core.js:14078)

我相信它与startWith添加有关,因为只要我取消该部分,错误就会消失。

我不知道错误是什么或如何修复它。提前感谢任何建议或帮助。

修改

Here's a link转到包含组件文件的文件夹,位于我的GitHub存储库中的正确分支上。

1 个答案:

答案 0 :(得分:0)

在测试您的存储库之后,我收到了一条不同的错误消息,该消息从以下开始。

  

错误:ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。上一个值:'null'。当前值:'0'。

基本上,变量this.$trackLengththis.$currentTimeDisplay& this.$playheadPosition从null开始并保持不变,直到ngAfterViewInit()触发,然后为它们定义管道并用'0'填充它 - 所有这些都在一个更改周期内。

我能够通过将startsWith(0)'碰撞'到下一个更改检测周期来修复它,方法是将delay(0)添加到最后。

注意 ExpressionChangedAfterItHasBeenCheckedError更多是警告,仅在开发模式下显示,但并不一定意味着错误在生产模式下消失。但是在你的代码中,这并不是一个真正的错误 - 变更检测检查只是过于热心。

initEvents() {
  this.$trackLength = Observable.fromEvent(this.audioPlayer.nativeElement, 'canplaythrough')
    .map(() => {
      this.isReadyForPlayback = true;
      return Math.ceil(this.audioPlayer.nativeElement.duration);
    })
    .startWith(0).delay(0);

  this.$currentTimeDisplay = Observable.fromEvent(this.audioPlayer.nativeElement, 'timeupdate')
    .map(() => {
      const current = this.audioPlayer.nativeElement.currentTime;
      const duration = this.audioPlayer.nativeElement.duration;
      if (current === duration) {
        return 0;
      }
      return Math.ceil(current);
    })
    .startWith(0).delay(0);

  this.$playheadPosition = Observable.fromEvent(this.audioPlayer.nativeElement, 'timeupdate')
    .map(() => {
      const percentAsDecimal =
        this.audioPlayer.nativeElement.currentTime / this.audioPlayer.nativeElement.duration;
      if (percentAsDecimal >= 1) {
        this.isPlaying = false;
        return 0;
      }
      return 100 * percentAsDecimal;
    })
    .startWith(0).delay(0);
}

ngAfterViewInit() {
  this.initEvents();
}

演示 StackBlitz
请注意,Chrome中的StackBlitz不会在同一个标​​签中预览。您需要使用“在新窗口中打开”。

<audio #audioPlayer也不应使用*ngIf,而是使用[hidden]

目前,您的app.component始终传入有效的audioSrc,但如果audioSrc无效,则会收到错误消息。

<audio #audioPlayer *ngIf="audioSrc" [src]="audioSrc"></audio>  

<audio #audioPlayer [hidden]="!audioSrc" [src]="audioSrc"></audio>  // use [hidden] instead

因为这依赖于在DOM中找到它

@ViewChild('audioPlayer') audioPlayer: ElementRef;