无法读取未定义的属性“viewContainerRef”

Posted

技术标签:

【中文标题】无法读取未定义的属性“viewContainerRef”【英文标题】:Cannot read property 'viewContainerRef' of undefined 【发布时间】:2018-06-28 02:01:24 【问题描述】:

我正在尝试显示与角度文档中的示例类似(不准确)的动态组件。

我有一个带有 viewContainerRef 的动态指令

@Directive(
   selector: '[dynamicComponent]'
)
export class DynamicComponentDirective 
   constructor(public viewContainerRef: ViewContainerRef)  

组件代码摘录

@ViewChild(DynamicComponentDirective) adHost: DynamicComponentDirective;
..
ngAfterViewInit() 
let componentFactory = null;
                console.log(component);
                componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
                // this.adHost.viewContainerRef.clear();
                const viewContainerRef = this.adHost.viewContainerRef;
                viewContainerRef.createComponent(componentFactory);

终于在模板中添加了<ng-template dynamicComponent></ng-template>

【问题讨论】:

您可能需要在此处添加this this.viewContainerRef.createComponent 【参考方案1】:

您可以采用这种方法: 不要创建指令,而是给 ng-template 一个 ID

<ng-template #dynamicComponent></ng-template>

在组件类中使用 @ViewChild 装饰器

@ViewChild('dynamicComponent',  read: ViewContainerRef ) myRef

ngAfterViewInit() 
    const factory = this.componentFactoryResolver.resolveComponentFactory(component);
    const ref = this.myRef.createComponent(factory);
    ref.changeDetectorRef.detectChanges();

【讨论】:

这行得通,非常感谢,但我很好奇它为什么行得通。我的指令选择器是正确的,但由于某种原因找不到。当我切换到 ID 选择器时,它立即选择了它。 不清楚componentresolveComponentFactory(component)中应该指什么 这对我有用..但是如何将数据传递给正在动态创建的组件? @Kingsley 您可以像这样在组件实例上设置属性:ref.instance.someInputData = data const viewContainerRef = this.adHost.viewContainerRef;这在某种程度上不适用于指令。当使用引用变量时,我们应该调用 const viewContainerRef = this.adHost; viewContainerRef.createComponent(componentFactory);工作得很好。【参考方案2】:

我遇到了同样的问题。您必须将指令添加到 AppModule 中:

 @NgModule(
  declarations: [
    AppComponent,
    ...,
    YourDirective,
  ],
  imports: [
   ...
  ],
  providers: [...],
  bootstrap: [AppComponent],
  entryComponents: [components to inject if required]
)

【讨论】:

解决了我的问题,在 Angular 教程中没有提到。谢谢 哇。好东西。这一定是公认的答案。谢谢。 注意常春藤这是不推荐使用的。【参考方案3】:

在 Angular 8 中,我的解决方法是:

@ViewChild('dynamicComponent', static: true, read: ViewContainerRef) container: ViewContainerRef;

【讨论】:

解决了我的问题 他们在更新指南 (update.angular.io/#7.0:8.0l3) 和静态查询迁移指南 (angular.io/guide/static-query-migration) 中提到了这一点,但您的示例使其更加清晰。 Angular 11,与 ng-if 相同的问题,通过添加类似的行解决了 @ViewChild('dynamicComponent', static: true) container: ViewContainerRef;【参考方案4】:

我也遇到了这个问题,原因是我想动态加载我的组件的位置是在最初隐藏的 ng-if 中。

<div *ngIf="items">
   <ng-template appInputHost></ng-template>
</div>

@ViewChild(InputHostDirective,  static: true ) inputHost: InputHostDirective;

将 ng-template 移到 ng-if 之外解决了问题。

【讨论】:

【参考方案5】:

在我的例子中,它是 Angular 9,指令的选择器在 *ngIf 中。我使用的解决方案是:

@ViewChild(ExampleDirective, static: false) exampleDirectiveHost: ExampleDirective;

Mark static 是假的,原因是这里定义了Angular documentation

我做的另一件事是使用

ngAfterViewInit()
   const viewContainerRef = this.exampleDirectiveHost.viewContainerRef;;

【讨论】:

我的问题是关于 ngOninit,视图容器引用未定义。一旦移动到 ngAfterViewInit() 就可以正常工作了。【参考方案6】:

请注意,如果指令选择器(在本例中为 dynamicComponent)位于:

    组件的第一个元素

    具有 *ngIf 条件的父元素

因此,我通过将它放在组件 html 中的非根标记中来避免它,并且仅在条件匹配时才将组件加载到 viewContainerRef。

【讨论】:

我遇到了viewContainerRef 元素的未定义错误,花了几个小时才找到问题所在。最后,看到你的答案,只是实例化了空对象,而不是用 *ngIf 和 viola 来检查它!谢谢你的回答!【参考方案7】:

问题还可能是viewRef(本例中为dynamicComponent)的选择器与使用componentFactoryResolver 的组件模板中指定的选择器不匹配。

【讨论】:

【参考方案8】:

当指令未构造时,您会看到此错误。您可以在指令的构造函数上设置断点并检查断点是否命中。如果不是,则意味着您没有正确加载指令。然后您可以检查正在加载指令的组件是否正确地将指令添加到模板中。

【讨论】:

【参考方案9】:

模板 HTML 文件:

<ng-template #containerToHostDynamicallyInjectedComponents"></ng-template>

TS 文件

私有组件引用:ComponentRef = null;

@ViewChild('containerToHostDynamicallyInjectedComponents', read: ViewContainerRef) hostContainer: ViewContainerRef;

constructor(private componentFactoryResolver:ComponentFactoryResolver) 


ngOnInit() 

 const componentFactory = 
    this.componentFactoryResolver.resolveComponentFactory(<COMPONENT_NAME_TO_CREATE>);
    this.hostContainer.clear(); // CLEAR ANY INJECTED COMPONENT IF EXISTED.
    this.componentRef = this.hostContainer.createComponent(componentFactory);



```
this.componentRef: => 存储动态创建组件的引用,以访问组件公共属性和方法,甚至订阅事件发射器

访问组件属性和方法使用:

 this.componentRef.instance.<PROPERTY_NAME>
 this.componentRef.instance.<METHOD_NAME>();

您甚至可以订阅@Output 事件发射器

this.componentRef.instance.<OUTPUT_PROPERTY_NAME>
.pipe(
  take(1)
)
.subscribe(response => console.log(response));

【讨论】:

【参考方案10】:

我只是在 afterViewInit() cicle 而不是 onInit() cicle 中添加了 viewContainerRef 内容,它对我有用:T

我正在使用 Angular v11

 ngAfterViewInit(): void 
        const viewContainerRef = this.exampleHost.viewContainerRef;
        viewContainerRef.clear();
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ExampleEntryComponent);
        viewContainerRef.createComponent(componentFactory);
    

【讨论】:

以上是关于无法读取未定义的属性“viewContainerRef”的主要内容,如果未能解决你的问题,请参考以下文章

`无法读取未定义的属性(读取'组件')`

NextJS:未捕获的类型错误:无法读取未定义的属性(读取“属性”)

使用地图时反应'无法读取未定义的属性'

如何使用自定义错误消息捕获“TypeError:无法读取未定义的属性(读取'0')”?

查询返回错误“无法读取未定义的属性(读取'节点')”

无法读取未定义的“已触及”属性