【问题标题】:ngOnInit is not called on dynamic component没有在动态组件上调用 ngOnInit
【发布时间】:2020-01-03 23:40:59
【问题描述】:

我在一个项目中使用 Angular 8,但我无法启动一个动态组件,因为 ngOnInit 没有被调用。我已经构建了一个名为 PlaceholderDirective 的指令,如下所示:

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

并在 ng-template 上使用了该指令:

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

保存 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;
}

实际的动态组件很简单,注册在AppModule的entryComponents部分。为了了解问题所在,我已将其缩小到最低限度:

@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;
}

最后,动态组件的 HTML:

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

问题是动态组件中的 ngOnInit 没有被调用,我有其余的数据要在那里初始化,这样我就可以构建模板的其余部分。

真正奇怪的是,如果我的控制台没有打开,而我打开它或反之亦然,模板就会显示出来。

【问题讨论】:

标签: angular angular-components angular-dynamic-components angular-lifecycle-hooks


【解决方案1】:

根据上面@codingandstuff 评论中的答案,只是想通过上面示例的代码更改更清楚地说明。

另外,我希望这个答案可以作为任何在谷歌搜索“如何生成/注入/渲染角度动态组件”的人的参考。

  1. 我们需要在指令中添加公共属性 changeDetectorRef:
@Directive({
  selector: '[appPlaceholder]'
})
export class PlaceholderDirective {
  constructor(
    public viewContainerRef: ViewContainerRef,
    public changeDetectorRef: ChangeDetectorRef,
  ) {}
}
  1. 现在我们需要在设置实例属性后使用它:
@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. 享受吧:)

如果您已经将 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();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-16
    • 1970-01-01
    • 2019-09-03
    • 2018-03-11
    • 2021-03-13
    • 2018-08-08
    • 1970-01-01
    • 2016-10-25
    相关资源
    最近更新 更多