简体   繁体   English

ngOnInit 未在动态组件上调用

[英]ngOnInit is not called on dynamic component

I'm using Angular 8 for a project, and I'm having trouble launching a dynamic component because ngOnInit doesn't get called.我在一个项目中使用 Angular 8,但我在启动动态组件时遇到了问题,因为 ngOnInit 没有被调用。 I've build a directive called PlaceholderDirective, shown below:我已经构建了一个名为 PlaceholderDirective 的指令,如下所示:

@Directive({
  selector: '[appPlaceholder]'
})
export class PlaceholderDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

And used that directive on a ng-template:并在 ng-template 上使用该指令:

<ng-template appPlaceholder></ng-template>

The component that holds the HTML where the ng-template lies launches the dynamic component, as such:包含 ng-template 所在 HTML 的组件启动动态组件,如下所示:

@ViewChild(PlaceholderDirective, {static: false}) private placeholder: PlaceholderDirective;
...
constructor(private componentResolver: ComponentFactoryResolver) {}
...
public launchSensorEditComponent(id: number) {
    const componentFactory = this.componentResolver.resolveComponentFactory(SensorEditComponent);
    const hostViewContainerRef = this.placeholder.viewContainerRef;
    hostViewContainerRef.clear();
    const sensorEditComponentRef = hostViewContainerRef.createComponent(componentFactory);
    sensorEditComponentRef.instance.sensorId = id;
}

The actual dynamic component is very simple, and its registered in the entryComponents section in AppModule.实际的动态组件非常简单,在AppModule的entryComponents部分注册。 I've narrow it down to the bare minimum in order to understand what's wrong:我已将其缩小到最低限度,以了解出了什么问题:

@Component({
  selector: 'app-sensor-edit',
  templateUrl: './sensor-edit.component.html',
  styleUrls: ['./sensor-edit.component.css']
})
export class SensorEditComponent implements OnInit {
  @Input() sensorId: number;
  private showDialog: boolean = false;

  constructor() {
    console.log('constructor');
  }

  ngOnInit() {
    console.log('ngOnInit');
    this.showDialog = true;
}

Last, the HTML for the dynamic component:最后,动态组件的 HTML:

<div *ngIf="showDialog">
  <h3>its not working</h3>
</div>

The problem is that ngOnInit from the dynamic component doesn't get called and I have the rest of the data to initialize in there, so that I can build the rest of the template.问题是动态组件中的 ngOnInit 没有被调用,我有其余的数据要在那里初始化,以便我可以构建模板的其余部分。

The really weird part is that if my console is not opened, and I open it or vice-versa, the template is shown.真正奇怪的部分是,如果我的控制台没有打开,而我打开它,反之亦然,则会显示模板。

Based on the answers in comment by @codingandstuff above, just want to make it more clear with code changes for the example above.根据上面@codingandstuff 在评论中的回答,只是想通过上面示例的代码更改使其更加清晰。

Also, I would like this answer to be here as a reference for anybody who googles "how to generate / inject / render dynamic component in angular".另外,我希望这个答案在这里作为任何在谷歌上搜索“如何以角度生成/注入/渲染动态组件”的参考。

  1. We need to add public property changeDetectorRef to the directive:我们需要将公共属性 changeDetectorRef 添加到指令中:
@Directive({
  selector: '[appPlaceholder]'
})
export class PlaceholderDirective {
  constructor(
    public viewContainerRef: ViewContainerRef,
    public changeDetectorRef: ChangeDetectorRef,
  ) {}
}
  1. Now we need to use it after setting instance properties:现在我们需要在设置实例属性后使用它:
@ViewChild(PlaceholderDirective, {static: false}) private placeholder: PlaceholderDirective;
...
constructor(private componentResolver: ComponentFactoryResolver) {}
...
public launchSensorEditComponent(id: number) {
    const componentFactory = this.componentResolver.resolveComponentFactory(SensorEditComponent);
    const hostViewContainerRef = this.placeholder.viewContainerRef;
    hostViewContainerRef.clear();
    const sensorEditComponentRef = hostViewContainerRef.createComponent(componentFactory);
    sensorEditComponentRef.instance.sensorId = id;
    this.placeholder.changeDetectorRef.detectChanges(); // <-- this line has been added
}
  1. Enjoy:)享受:)

And if you've upgraded Angular to 13+, then please note that now you can provide component class directly to createComponent without dealing with componentResolver, componentFactory etc. Just like this:如果你已经将 Angular 升级到 13+,那么请注意,现在你可以直接将组件类提供给 createComponent 而不需要处理 componentResolver、componentFactory 等。就像这样:

public launchSensorEditComponent(id: number) {
    const { hostViewContainerRef, changeDetectorRef } = this.placeholder;
    hostViewContainerRef.clear();
    const sensorEditComponentRef = hostViewContainerRef.createComponent(SensorEditComponent);
    sensorEditComponentRef.instance.sensorId = id;
    changeDetectorRef.detectChanges();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM