我正在为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存储库中的正确分支上。
答案 0 :(得分:0)
在测试您的存储库之后,我收到了一条不同的错误消息,该消息从以下开始。
错误:ExpressionChangedAfterItHasBeenCheckedError:表达式在检查后发生了变化。上一个值:'null'。当前值:'0'。
基本上,变量this.$trackLength
,this.$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;