在* ngIf中加载动态组件-createComponent未定义

时间:2019-10-31 15:45:27

标签: angular angular8 angular-ng-if

当用户单击如下所示的按钮时,我正在尝试将动态组件加载到视图中:

<button (click)="showContent()">Show Panel</button>
<!--This starts as 'showPanel = false'-->
<div *ngIf="showPanel">
    <ng-template #ref></ng-template>
</div>

但是,当我单击视图中的按钮时,我尝试运行loadComponent(),但出现错误,提示Cannot read property 'createComponent' of undefined

我已经找到了一些解决方案,有人建议使用QueryList<ViewContainerRef>,但是我没有成功使它工作。

  

来源:ViewContainerRef is undefined when called in ngAfterViewInit

另一种解决方案建议使用ElementRef并检查Chaged状态,但是即使尝试在ngAfterViewInit中对其进行检查,也始终不确定。

  

来源:@ViewChild in *ngIf

我正在寻找可用于Angular 8的选项,但我不确定下一步要去哪里。

以下代码:

parent.component.ts

export class ParentComponent {

@ViewChild('ref', { static: false, read: ViewContainerRef }) ref: ViewContainerRef;

showPanel = false;

loadComponent(): void {
    const factory = this.componentFactoryResolver.resolveComponentFactory(ChildComponent);
    const component = this.ref.createComponent(factory);
    component.changeDetectorRef.detectChanges();
}

showContent(): void {
    this.showPanel = !this.showPanel;
    this.loadContent(); // Error is thrown here, this doesn't make sense as *ngIf should now be true.
}

2 个答案:

答案 0 :(得分:1)

按照yurzui的想法,这是我将其应用的方式:

Here是StackBlitz示例

changeDetectorRef.detectChanges()

showContent () {
    this.showPanel = !this.showPanel;

   if (this.showPanel) {
      this.cdr.detectChanges();
      this.loadComponent();
    }
  }

ViewChild的设置器

  private _ref: ViewContainerRef;

  private get ref () {
    return this._ref;
  }

  @ViewChild('ref', { static: false, read: ViewContainerRef })
  private set ref (r) {
    console.log('setting ref', r)
    this._ref = r;

    if (this._ref) {
      this.loadComponent();
    }
  }

  showPanel = false;

  constructor (
    private cdr: ChangeDetectorRef,
    private cfr: ComponentFactoryResolver,
  ) { }

  loadComponent () {
    const factory = this.cfr.resolveComponentFactory(ChildComponent);
    const component = this.ref.createComponent(factory);
  }

  showContent () {
    this.showPanel = !this.showPanel;
  }

使用<ng-container>

正如您所指出的那样,使用ngTemplateOutlet通常是一个很好的解决方案,但是当您处理多个组件时,执行模板中的所有逻辑将变得很麻烦。

我们可以利用ViewContainerRef's API来处理组件(.ts file)中的所有内容。

<button (click)="showContent()">Show Panel</button>

<ng-container #vcr></ng-container>
@ViewChild('vcr', { static: true, read: ViewContainerRef })
 vcr: ViewContainerRef;

 showContent () {
  this.showPanel = !this.showPanel;  

  this.showPanel && this.attachComponent();

  !this.showPanel && this.removeComponent();  
}

private attachComponent () {
  const compFactory = this.cfr.resolveComponentFactory(ChildComponent);

  const compView = this.vcr.createComponent(compFactory);
}

private removeComponent () {
    this.vcr.clear();
}

这种方法给您带来了更多无法控制的控制权!
例如,您可以使用showPanelvcr.detach来保留组件vcr.insert变为假之后的状态。

You can find how right here

答案 1 :(得分:0)

这是因为* ngIf在条件评估为false时删除了div元素,这意味着子元素在组件模板中不存在。

您可以使用[hidden]代替它仅隐藏div元素,以便您可以通过模板引用变量进行访问。

<button (click)="showContent()">Show Panel</button>
<!--This starts as 'showPanel = false'-->
<div [hidden]="!showPanel">
    <ng-template #ref></ng-template>
</div>