Angular 5 - 动态组件加载器 - 无法读取未定义的属性“viewContainerRef" [英] Angular 5 - Dynamic component loader - Cannot read property 'viewContainerRef' of undefined

查看:52
本文介绍了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">&times;</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

<button class="btn btn-outline-success new-checklist-btn">{{ 'New Checklist'|翻译}}</button>

<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">&times;</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屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆