@ViewChild未定义

时间:2017-02-14 05:02:53

标签: angular typescript

我正在尝试使用@ViewChild切换*ngIf元素,然后调用本机事件。

这是我的html元素,用#audioPlayer装饰,因此我可以通过@ViewChild提取元素。

<audio 
    #audioPlayer     
    *ngIf="conversationIsRunning"    
    [src]='activeConversation?.clips[activeConversation.activeClipId].audio.blob'
    (ended)="audioComplete($event)" 
    autoplay controls preload="auto" >
</audio>

在我的打字稿中,我有以下内容:

@ViewChild('audioPlayer') audioPlayer;
private conversationIsRunning: boolean;

ngOnInit() {
    this.conversationIsRunning = false;
}

ngOnChanges() {
    console.log("ngOnChanges");
    this.conversationIsRunning = true;       
    console.log(this.audioPlayer); // undefined
    this.audioPlayer.nativeElement.play(); // error
}

如果我从*ngIf元素中删除audio,则错误消失。但是我真的希望这个功能在我不需要的时候销毁元素。

我在this回答中看到你可以在@ViewChild上使用setter,所以我实现了它,但没有成功...

private privAudioPlayer: ViewContainerRef;
@ViewChild('audioPlayer') set audioPlayer(audioPlayer: ViewContainerRef) {
    this.privAudioPlayer = audioPlayer;
    console.log('audioPlayer set called >> ', audioPlayer, this.privAudioPlayer);
};

...但是这总是输出audioPlayer set called >> undefined undefined

我还尝试将this.conversationIsRunning = true;从其当前位置拆分并放入各种不同的生命周期钩子,然后将ngOnChanges更改为其他生命周期钩子也无济于事。

我必须等到下一帧还是什么?为什么set audioPlayer mutator会收到undefined

2 个答案:

答案 0 :(得分:0)

在第一个代码示例中,您应该使用ngAfterViewInit()而不是ngOnChanges()

答案 1 :(得分:0)

问题在于,作为动态元素,初始化组件时该元素不存在,因此使用未定义的ElementRef创建视图子级。

有一些解决方案。

如果您运行的是Angular 8,则可以简单地告诉它元素不是静态的,并且当它存在时,视图子级将收到ElementRef:

@ViewChild('audioPlayer', { static: false }) audioPlayer: ElementRef;

在Angular 8之前,最好的方法是将条件元素放在子组件中,并将所有必需的数据作为输入参数传递:

<app-audio *ngIf="conversationIsRunning"> </app-audio>

子组件具有如下模板:

<audio #audioPlayer [src]='activeConversation?.clips[activeConversation.activeClipId].audio.blob'
(ended)="audioComplete($event)" 
autoplay controls preload="auto" >
</audio>

您的事件处理程序将位于子代码的后面,并且您可以公开一个方法来响应父组件中的ngChanges进行播放。如果您需要与父级进行通讯,也可以发布事件。该元素始终存在于子组件中,因此ElementRef将始终有效。

最后,您也可以只切换样式显示属性。该组件始终存在于DOM中,并且视图子级将始终具有有效的ElementRef。显然,它总是使用资源并导致负载开销,无论您是否显示它:

<audio 
#audioPlayer     
[style.display]="conversationIsRunningDisplay()"   [src]='activeConversation?.clips[activeConversation.activeClipId].audio.blob'
(ended)="audioComplete($event)" 
autoplay controls preload="auto" >
</audio>

代码如下:

@ViewChild('audioPlayer') audioPlayer;
private conversationIsRunning: boolean;

ngOnInit() {
    this.conversationIsRunning = false;
}

ngOnChanges() {
    console.log("ngOnChanges");
    this.conversationIsRunning = true;       
    console.log(this.audioPlayer); // undefined
    this.audioPlayer.nativeElement.play(); // error
}

public conversationIsRunningDisplay() {
    if (this.conversationIsRunning) {
       return 'block';
    }
    return 'none';
}