Angular 5 - 动态组件加载器 - 无法读取未定义的属性“viewContainerRef"
[英] Angular 5 - Dynamic component loader - Cannot read property 'viewContainerRef' of undefined
本文介绍了Angular 5 - 动态组件加载器 - 无法读取未定义的属性“viewContainerRef"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在尝试在 Angular 5 中动态加载组件.我使用 Angular Guide 为此,但我现在卡住了.事情是;我收到以下错误:
<块引用>
错误类型错误:无法读取未定义的属性viewContainerRef"
错误在我的 ChecklistComponent
处
const viewContainerRef = this.appHost.viewContainerRef;
出于某种原因 appHost
未定义,我不知道为什么.
这是我的其余代码:
checklist.component.ts
import {AfterViewInit, Component, ComponentFactoryResolver, Input, OnInit, ViewChild,视图封装来自'@angular/core';从./checklist.directive"导入{ChecklistDirective};从./checklist-main/checklist-main.component"导入{ChecklistMainComponent};@成分({选择器:应用清单",templateUrl: './checklist.component.html',styleUrls: ['./checklist.component.scss'],封装:ViewEncapsulation.None})导出类 ChecklistComponent 实现 OnInit {@ViewChild('modalElement') modalElement;@ViewChild(ChecklistDirective) appHost:ChecklistDirective;构造函数(私有 componentFactoryResolver:ComponentFactoryResolver){}ngOnInit() {this.loadComponent();}加载组件(){const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChecklistMainComponent);const viewContainerRef = this.appHost.viewContainerRef;viewContainerRef.clear();const componentRef = viewContainerRef.createComponent(componentFactory);}}
checklist.component.html
<div class="modal-header"><h4 class="modal-title">{{ '清单'|翻译 }}<button type="button" class="close" aria-label="Close" (click)="d('Cross click')"><span aria-hidden="true">×</span>按钮>
<div class="modal-body"><ng-template appHost></ng-template>
<div class="modal-footer"><button type="button" class="btn btn-default" (click)="c('Close click')">{{ 'Close'|翻译}}</button><button type="button" class="btn btn-primary" (click)="onConfirm($event)">{{ 'Confirm'|翻译}}</button>
</ng-模板>
checklist.directive.ts
import {Directive, ViewContainerRef} from '@angular/core';@指示({选择器:'[appHost]'})导出类 ChecklistDirective {构造函数(公共viewContainerRef:ViewContainerRef){}}
checklist.module.ts
import { NgModule } from '@angular/core';从'@angular/common'导入{CommonModule};从 './checklist.component' 导入 { ChecklistComponent };从'../../theme/directives/directives.module'导入{DirectivesModule};从@ngx-translate/core"导入 {TranslateModule};从'../../pipes/common.pipes.module'导入{AppCommonPipesModule};从'../task/shared.task.module'导入{SharedTaskModule};@NgModule({进口:[通用模块,指令模块,共享任务模块,翻译模块,AppCommonPipesModule,],声明: [清单组件]})导出类 ChecklistModule { }
shared.checklist.module.ts
从'@angular/core'导入{NgModule};从@angular/common"导入 {CommonModule};从'ngx-perfect-scrollbar'导入{PerfectScrollbarModule};从@swimlane/ngx-datatable"导入 { NgxDatatableModule };从@angular/forms"导入 { FormsModule, ReactiveFormsModule };从'../../components/form/form.module'导入{FormModule};从'angular-2-dropdown-multiselect'导入{ MultiselectDropdownModule };从'../../theme/directives/directives.module'导入{DirectivesModule};从 'ng-pick-datetime' 导入 { OwlDateTimeModule, OwlNativeDateTimeModule };从@ng-bootstrap/ng-bootstrap"导入 {NgbModule};从'../../components/model/shared.modal.module'导入{SharedModalModule};从'../../pipes/common.pipes.module'导入{AppCommonPipesModule};从'../../directives/shared.directive.module'导入{SharedDirectiveModule};从 '@ngx-translate/core' 导入 { TranslateModule };从./checklist.component"导入{ChecklistComponent};从./checklist-main/checklist-main.component"导入{ChecklistMainComponent};从./checklist-view/checklist-view.component"导入{ChecklistViewComponent};从./checklist-mutate/checklist-mutate.component"导入{ChecklistMutateComponent};从./checklist.directive"导入{ChecklistDirective};@NgModule({进口:[通用模块,完美滚动条模块,NgxDatatable 模块,表单模块,表单模块,反应形式模块,多选下拉模块,指令模块,猫头鹰日期时间模块,OwlNativeDateTimeModule,Ngb 模块,共享模态模块,翻译模块,AppCommonPipesModule,共享指令模块],声明: [清单组件,清单MainComponent,清单视图组件,ChecklistMutateComponent,清单指令],出口:[清单组件,清单MainComponent,清单视图组件,ChecklistMutateComponent,清单指令],entryComponents: [ChecklistMainComponent]})导出类 SharedChecklistModule {}
checklist-main.component.ts
import {Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation} from '@angular/core';进口 {集合HttpRequestOptions、集合HttpResponse、按请求排序选项}来自'../../../services/http.service';从'../../../models/checklist.models'导入{ChecklistModel};从../../../services/checklist.service"导入{ChecklistService};@成分({选择器:'app-checklist-main',templateUrl: './checklist-main.component.html',styleUrls: ['./checklist-main.component.scss'],封装:ViewEncapsulation.None})导出类 ChecklistMainComponent 实现 OnInit {isLoading = false;tableInfo: any = {offset: 0, limit: 10};collectionRequestModel = new CollectionHttpRequestOptions();collectionResponseModel = new CollectionHttpResponse();@ViewChild('myTable') 表:任何;@Input() formData: ChecklistModel = new ChecklistModel();@Output() activate = new EventEmitter();构造函数(私有 checklistService:ChecklistService){}ngOnInit() {this.setPage(this.tableInfo);}公共搜索(事件){this.collectionRequestModel.page = 1;this.collectionRequestModel.search = event.target.value;this.load();}加载() {this.isLoading = true;this.checklistService.getCollection(this.collectionRequestModel).subscribe(response => {this.collectionResponseModel = 响应;this.isLoading = false;});}onActivate(事件){this.activate.emit(事件);if (event.type === "click") {this.loadChecklist(事件);}}setPage(pageInfo: any) {this.collectionRequestModel.page = (pageInfo.offset + 1);this.collectionRequestModel.itemsPerPage = pageInfo.limit;this.load();}onSort(事件:任何){const sort = event.sorts[0];const orderBy = new OrderByRequestOptions();orderBy.key = sort.prop;orderBy.direction = sort.dir.toUpperCase();this.collectionRequestModel.orderBy = [orderBy];this.load();}加载清单(事件){console.log("耶");}}
checklist-main.component.html
<div class="body"><h5>当前检查清单</h5><ngx-datatable #myTable class="material" [rows]="collectionResponseModel.items" [columnMode]="'force'"[headerHeight]="50"[footerHeight]="50" [rowHeight]="'fixed'" [externalPaging]="true"[count]="collectionResponseModel.totalCount" [offset]="tableInfo.offset"[limit]="tableInfo.limit" [loadingIndicator]="isLoading" (activate)="onActivate($event)"(page)='setPage($event)' (sort)='onSort($event)'><ngx-datatable-column name="Naam" prop="name" [flexGrow]="1"><ng-template let-row="row" let-value="value" ngx-datatable-cell-template>{{价值}}</ng-模板></ngx-datatable-column></ngx-datatable>
解决方案
you are using ng-template
(<ng-template #modalElement let-c="close" let-d="dismiss">
) 直到你告诉 angular 这样做才会被渲染.这意味着使用该指令的第二个/嵌套 ng-template
永远不会呈现,因为外部 ng-template
没有呈现.
看看 https://stackblitz.com/edit/angular-tjr4uq如果您删除 ngOnInit
中的代码,您将看到 a) 外部 ng-template
中没有任何内容被渲染,并且 b) element
将是ngAfterViewInit
中未定义
编辑(也许尝试一下):
import {AfterViewInit, ViewContainerRef, Component, ComponentFactoryResolver, Input, OnInit, ViewChild,视图封装来自'@angular/core';import { ChecklistDirective } from "./checklist.directive";import { ChecklistMainComponent } from "./checklist-main/checklist-main.component";@成分({选择器:应用清单",templateUrl: './checklist.component.html',styleUrls: ['./checklist.component.scss'],封装:ViewEncapsulation.None})导出类 ChecklistComponent 实现 OnInit {@ViewChild('modalElement') modalElement;@ViewChild(ChecklistDirective) appHost:ChecklistDirective;构造函数(私有 vc:ViewContainerRef,私有 componentFactoryResolver:ComponentFactoryResolver){}ngOnInit() {this.vc.createEmbeddedView(this.modalElement);}ngAfterViewInit() {this.loadComponent();}加载组件(){const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChecklistMainComponent);const viewContainerRef = this.appHost.viewContainerRef;viewContainerRef.clear();const componentRef = viewContainerRef.createComponent(componentFactory);}}
I am trying to make the dynamically load components in Angular 5. I use the Angular Guide for this, but I am stuck now. The thing is; I get the following error:
ERROR TypeError: Cannot read property 'viewContainerRef' of undefined
The error is in my ChecklistComponent
at
const viewContainerRef = this.appHost.viewContainerRef;
For some reason appHost
is undefined and I have no idea why.
Here is the rest of my code:
checklist.component.ts
import {
AfterViewInit, Component, ComponentFactoryResolver, Input, OnInit, ViewChild,
ViewEncapsulation
} from '@angular/core';
import {ChecklistDirective} from "./checklist.directive";
import {ChecklistMainComponent} from "./checklist-main/checklist-main.component";
@Component({
selector: 'app-checklist',
templateUrl: './checklist.component.html',
styleUrls: ['./checklist.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ChecklistComponent implements OnInit {
@ViewChild('modalElement') modalElement;
@ViewChild(ChecklistDirective) appHost: ChecklistDirective;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
this.loadComponent();
}
loadComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChecklistMainComponent);
const viewContainerRef = this.appHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
}
}
checklist.component.html
<ng-template #modalElement let-c="close" let-d="dismiss">
<div class="modal-header">
<h4 class="modal-title">{{ 'Checklist'| translate }}</h4>
<button type="button" class="close" aria-label="Close" (click)="d('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ng-template appHost></ng-template>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" (click)="c('Close click')">{{ 'Close'| translate }}</button>
<button type="button" class="btn btn-primary" (click)="onConfirm($event)">{{ 'Confirm'| translate }}</button>
</div>
</ng-template>
checklist.directive.ts
import {Directive, ViewContainerRef} from '@angular/core';
@Directive({
selector: '[appHost]'
})
export class ChecklistDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
checklist.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ChecklistComponent } from './checklist.component';
import {DirectivesModule} from '../../theme/directives/directives.module';
import {TranslateModule} from '@ngx-translate/core';
import {AppCommonPipesModule} from '../../pipes/common.pipes.module';
import {SharedTaskModule} from '../task/shared.task.module';
@NgModule({
imports: [
CommonModule,
DirectivesModule,
SharedTaskModule,
TranslateModule,
AppCommonPipesModule,
],
declarations: [
ChecklistComponent
]
})
export class ChecklistModule { }
shared.checklist.module.ts
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {PerfectScrollbarModule} from 'ngx-perfect-scrollbar';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {FormModule} from '../../components/form/form.module';
import { MultiselectDropdownModule } from 'angular-2-dropdown-multiselect';
import {DirectivesModule} from '../../theme/directives/directives.module';
import { OwlDateTimeModule, OwlNativeDateTimeModule } from 'ng-pick-datetime';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {SharedModalModule} from '../../components/model/shared.modal.module';
import {AppCommonPipesModule} from '../../pipes/common.pipes.module';
import {SharedDirectiveModule} from '../../directives/shared.directive.module';
import { TranslateModule } from '@ngx-translate/core';
import {ChecklistComponent} from "./checklist.component";
import {ChecklistMainComponent} from "./checklist-main/checklist-main.component";
import {ChecklistViewComponent} from "./checklist-view/checklist-view.component";
import {ChecklistMutateComponent} from "./checklist-mutate/checklist-mutate.component";
import {ChecklistDirective} from "./checklist.directive";
@NgModule({
imports: [
CommonModule,
PerfectScrollbarModule,
NgxDatatableModule,
FormsModule,
FormModule,
ReactiveFormsModule,
MultiselectDropdownModule,
DirectivesModule,
OwlDateTimeModule,
OwlNativeDateTimeModule,
NgbModule,
SharedModalModule,
TranslateModule,
AppCommonPipesModule,
SharedDirectiveModule
],
declarations: [
ChecklistComponent,
ChecklistMainComponent,
ChecklistViewComponent,
ChecklistMutateComponent,
ChecklistDirective
],
exports: [
ChecklistComponent,
ChecklistMainComponent,
ChecklistViewComponent,
ChecklistMutateComponent,
ChecklistDirective
],
entryComponents: [ChecklistMainComponent]
})
export class SharedChecklistModule {
}
checklist-main.component.ts
import {Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation} from '@angular/core';
import {
CollectionHttpRequestOptions, CollectionHttpResponse,
OrderByRequestOptions
} from '../../../services/http.service';
import {ChecklistModel} from '../../../models/checklist.models';
import {ChecklistService} from '../../../services/checklist.service';
@Component({
selector: 'app-checklist-main',
templateUrl: './checklist-main.component.html',
styleUrls: ['./checklist-main.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ChecklistMainComponent implements OnInit {
isLoading = false;
tableInfo: any = {offset: 0, limit: 10};
collectionRequestModel = new CollectionHttpRequestOptions();
collectionResponseModel = new CollectionHttpResponse<ChecklistModel>();
@ViewChild('myTable') table: any;
@Input() formData: ChecklistModel = new ChecklistModel();
@Output() activate = new EventEmitter();
constructor(private checklistService: ChecklistService) {}
ngOnInit() {
this.setPage(this.tableInfo);
}
public onSearch(event) {
this.collectionRequestModel.page = 1;
this.collectionRequestModel.search = event.target.value;
this.load();
}
load() {
this.isLoading = true;
this.checklistService.getCollection(this.collectionRequestModel).subscribe(response => {
this.collectionResponseModel = response;
this.isLoading = false;
});
}
onActivate(event) {
this.activate.emit(event);
if (event.type === "click") {
this.loadChecklist(event);
}
}
setPage(pageInfo: any) {
this.collectionRequestModel.page = (pageInfo.offset + 1);
this.collectionRequestModel.itemsPerPage = pageInfo.limit;
this.load();
}
onSort(event: any) {
const sort = event.sorts[0];
const orderBy = new OrderByRequestOptions();
orderBy.key = sort.prop;
orderBy.direction = sort.dir.toUpperCase();
this.collectionRequestModel.orderBy = [orderBy];
this.load();
}
loadChecklist(event) {
console.log("Yay");
}
}
checklist-main.component.html
<div class="header">
<button class="btn btn-outline-success new-checklist-btn">{{ 'New Checklist'| translate }}</button>
</div>
<div class="body">
<h5>Current Checklists</h5>
<ngx-datatable #myTable class="material" [rows]="collectionResponseModel.items" [columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50" [rowHeight]="'fixed'" [externalPaging]="true"
[count]="collectionResponseModel.totalCount" [offset]="tableInfo.offset"
[limit]="tableInfo.limit" [loadingIndicator]="isLoading" (activate)="onActivate($event)"
(page)='setPage($event)' (sort)='onSort($event)'>
<ngx-datatable-column name="Naam" prop="name" [flexGrow]="1">
<ng-template let-row="row" let-value="value" ngx-datatable-cell-template>
{{value}}
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
解决方案
you are using ng-template
(<ng-template #modalElement let-c="close" let-d="dismiss">
) which will not be rendered until you tell angular to do so.
this means the second/nested ng-template
which uses the directive is never rendered because the outer ng-template
is not rendered.
Have a look at https://stackblitz.com/edit/angular-tjr4uq
if you remove the code in ngOnInit
you'll see that a) nothing within the outer ng-template
is rendered and b) element
will be undefined in ngAfterViewInit
EDIT (maybe try that):
import {
AfterViewInit, ViewContainerRef, Component, ComponentFactoryResolver, Input, OnInit, ViewChild,
ViewEncapsulation
} from '@angular/core';
import { ChecklistDirective } from "./checklist.directive";
import { ChecklistMainComponent } from "./checklist-main/checklist-main.component";
@Component({
selector: 'app-checklist',
templateUrl: './checklist.component.html',
styleUrls: ['./checklist.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ChecklistComponent implements OnInit {
@ViewChild('modalElement') modalElement;
@ViewChild(ChecklistDirective) appHost: ChecklistDirective;
constructor(private vc: ViewContainerRef, private componentFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
this.vc.createEmbeddedView(this.modalElement);
}
ngAfterViewInit() {
this.loadComponent();
}
loadComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(ChecklistMainComponent);
const viewContainerRef = this.appHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory);
}
}
这篇关于Angular 5 - 动态组件加载器 - 无法读取未定义的属性“viewContainerRef"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!