模板变量始终未定义ngOnChanges

时间:2019-05-08 19:21:02

标签: javascript angular angular-template lifecycle-hook

我有一个子组件,在其父组件中使用了两次,它使用ngOnChages更新值;

这一切都很好,但是根据通过@Input变量传递的信息,现在我正在尝试在子级中操作模板。

我发现的是;首次加载父组件时,在函数调用内模板变量未定义

这是有道理的,因为在onChanges方法中调用了此函数,并且没有给模板足够的加载时间。

我需要重构代码,但是如何?

目前,如果我使用setTimeout

所以简单地:

@ViewChild('myContainer', {read: ElementRef}) myContainer: ElementRef;
@Input() myUpdatingVar

ngOnChanges(): void {
  if (this.myUpdatingVar === someValue) {
      this.myFunc(this.someValue1);
   } else {
      this.myFunc(this.someValue2);
   }
}

myFunc(param){
   do stuff....
   this.updateDom(anotherParam);
}

updateDom(param) {
// If I use setTimeout this works
this.myContainer.nativeElement.querySelector(`#someId`); // Undefined
}

5 个答案:

答案 0 :(得分:1)

查看本指南:https://angular.io/guide/lifecycle-hooks

角度生命周期事件按特定顺序触发,并且某些事件在dom元素加载之前发生。

ngAfterViewInit是使您知道DOM(或至少您看到的抽象)已准备好进行查询的挂钩。这意味着在触发此事件之前,模板变量和viewchildren等无法正常工作。

编辑: 听起来您了解时间了。为了解决这个问题,我建议在onChanges中使用模板变量之前先检查是否已定义模板变量(现在,它将在以后对onChanges的所有调用中发生),然后如果需要在开始时运行一次,请在ngAfterViewInit中执行相同的操作。

答案 1 :(得分:1)

您可以使用ngOnChanges来检测更改,而不是使用setter

这是您需要做的

@Input()
set myUpdatingVar(value: any) {
   if(value) {
        this.myFunc(value);
   }
}

它不仅会在初始化视图时加载,而且在myUpdatingVar中进行任何更改时,Angular都会运行setter并执行您的函数。另外,ngOnChanges是昂贵的生命周期挂钩。如果不小心使用它,它将在组件中发生任何更改时运行。

但是,如果要使用ngOnChanges,则应使用它在变量中检测到的更改,而不是使用变量本身。

这里是例子

ngOnChanges(changes: SimpleChanges): void {
  if (changes.myUpdatingVar.currentValue === someValue) {
      this.myFunc(changes.myUpdatingVar.currentValue);
   } else {
      this.myFunc(changes.myUpdatingVar.currentValue);
   }
}

您可以检查是否是第一次更改,还可以获取currentValuepreviousValue,在这种情况下非常有用。

希望我的回答以某种方式对您有所帮助。 祝您编码愉快!

答案 2 :(得分:1)

使用更新的angular(9.x),我通过将static设置为true解决了这个问题:

@ViewChild('myContainer', {static: true})

答案 3 :(得分:0)

对于DOM操作,您应该使用ngAfterViewInit生命周期挂钩,以确保模板已加载到DOM中。

ngOnChanges提前触发一次,甚至在ngOnInit之前触发。对于DOM操作而言还为时过早。

您可以使用isViewInitialized布尔值,并在ngAfterViewInit中将其设置为true,然后在if中的ngOnChanges条件下使用它。

答案 4 :(得分:0)

正如其他人所说,onchanges运行太早,无法访问viewchildren,您将需要以下内容:

<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

<h2 
class="headline"
on="tap:list.toggleClass(class='my-custom-class')">
<ul id="list"></ul>

这样,如果尚未初始化视图,则跳过逻辑,并在初始化视图后手动触发ngOnChanges运行。

所有这一切,除非您正在与一些集成度不佳的第三方库互动,否则可能会有更多角度的方法来实现此目标。