如何在 Angular 8 中捕获动态渲染组件输出?
How to catch a dynamic rendered component's output in Angular 8?
我想捕捉 ViewChild
渲染组件的输出。 ngIf
触发后显示的 ViewChild 内容。
这是我的模板代码:
<div *ngIf="isModalVisible" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<!-- ... -->
</div>
<div class="modal-body">
<ng-template #dialogHost (saveModel)="setModel($event)"></ng-template>
</div>
</div>
</div>
</div>
这是 TypeScript 文件:
import { ChangeDetectorRef, Component, ComponentFactoryResolver, Input, ViewChild, ViewContainerRef } from '@angular/core';
import { DialogContentItem } from 'src/app/models/dialog/content/dialog-content-item';
@Component({
selector: 'app-dialog-preparator',
templateUrl: './dialog-preparator.component.html',
styleUrls: ['./dialog-preparator.component.css']
})
export class DialogPreparatorComponent {
isModalVisible = false;
@Input() dialogContent: DialogContentItem;
@ViewChild('dialogHost', {static: false, read: ViewContainerRef}) dialogHost: ViewContainerRef;
showModal() {
this.isModalVisible = true;
this.changeDetector.detectChanges();
this.renderComponent();
}
renderComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dialogContent.component);
this.dialogHost.clear();
const componentRef = this.dialogHost.createComponent(componentFactory);
(componentRef.instance as DialogContentInterface).data = this.dialogContent.data;
}
setModel(model: any) {
// I need to emit the model
}
}
这是 ProcutCreateEditComponent:
<app-dialog-preparator [dialogContent]="userDialogContent"></app-dialog-preparator>
此组件的 TypeScript 代码:
import { Component } from '@angular/core';
import { UserCreateEditComponent } from 'src/app/components/user/user-create-edit.component';
@Component({
selector: 'app-product-create-edit',
templateUrl: './product-create-edit.component.html',
styleUrls: ['./product-create-edit.component.scss']
})
export class ProductCreateEditComponent {
// this could be any other component in my software
userDialogContent: DialogContentItem = new DialogContentItem(UserCreateEditComponent, {});
constructor() {}
// ...
}
这是 UserCreateEditComponent 的 TypeScript 代码:
import { Component, Output } from '@angular/core';
@Component({
selector: 'app-user-create-edit',
templateUrl: './user-create-edit.component.html',
styleUrls: ['./user-create-edit.component.scss']
})
export class UserCreateEditComponent {
@Output() saveModel: EventEmitter<T> = new EventEmitter();
user = new User();
constructor() {}
save() {
this.saveModel.emit(this.user);
}
}
因此,在 ProcutCreateEditComponent 中,我创建了一个 UserCreateEditComponent 实例,并将该实例传递给 DialogPreparatorComponent。 DialogPreparatorComponent 在对话框中显示 UserCreateEditComponent,然后我单击保存按钮(在 UserCreateEditComponent 中),saveModel()
发送到 DialogPreparatorComponent。我想......但我收到的不是这个错误消息:
Event binding saveModel not emitted by any directive on an embedded template.
Make sure that the event name is spelled correctly and all directives are
listed in the "@NgModule.declarations". ("
</div>
<div class="modal-body">
<ng-template #dialogHost [ERROR ->](saveModel)="setModel($event)"></ng-template>
</div>
</div>
其实很好,因为 ng-template
没有 saveModel()
输出。
但是我怎样才能捕捉到 UserCreateEditComponent 的输出呢?
由于您是在代码中创建组件,因此您无法在模板中添加事件处理程序,您必须在创建组件时也将其添加到组件代码中。
例如,您可以更改渲染组件方法以同时添加订阅。
renderComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dialogContent.component);
this.dialogHost.clear();
const componentRef = this.dialogHost.createComponent(componentFactory);
(componentRef.instance as DialogContentInterface).data = this.dialogContent.data;
(componentRef.instance as DialogContentInterface).saveModel.subscribe((model) => this.setModel(model));
}
希望我正确理解了您的模型,并且您在此处创建的组件具有输出 属性。
确保在销毁动态组件时也取消订阅,这样您就不会因为剩余的订阅而导致内存泄漏。
我想捕捉 ViewChild
渲染组件的输出。 ngIf
触发后显示的 ViewChild 内容。
这是我的模板代码:
<div *ngIf="isModalVisible" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<!-- ... -->
</div>
<div class="modal-body">
<ng-template #dialogHost (saveModel)="setModel($event)"></ng-template>
</div>
</div>
</div>
</div>
这是 TypeScript 文件:
import { ChangeDetectorRef, Component, ComponentFactoryResolver, Input, ViewChild, ViewContainerRef } from '@angular/core';
import { DialogContentItem } from 'src/app/models/dialog/content/dialog-content-item';
@Component({
selector: 'app-dialog-preparator',
templateUrl: './dialog-preparator.component.html',
styleUrls: ['./dialog-preparator.component.css']
})
export class DialogPreparatorComponent {
isModalVisible = false;
@Input() dialogContent: DialogContentItem;
@ViewChild('dialogHost', {static: false, read: ViewContainerRef}) dialogHost: ViewContainerRef;
showModal() {
this.isModalVisible = true;
this.changeDetector.detectChanges();
this.renderComponent();
}
renderComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dialogContent.component);
this.dialogHost.clear();
const componentRef = this.dialogHost.createComponent(componentFactory);
(componentRef.instance as DialogContentInterface).data = this.dialogContent.data;
}
setModel(model: any) {
// I need to emit the model
}
}
这是 ProcutCreateEditComponent:
<app-dialog-preparator [dialogContent]="userDialogContent"></app-dialog-preparator>
此组件的 TypeScript 代码:
import { Component } from '@angular/core';
import { UserCreateEditComponent } from 'src/app/components/user/user-create-edit.component';
@Component({
selector: 'app-product-create-edit',
templateUrl: './product-create-edit.component.html',
styleUrls: ['./product-create-edit.component.scss']
})
export class ProductCreateEditComponent {
// this could be any other component in my software
userDialogContent: DialogContentItem = new DialogContentItem(UserCreateEditComponent, {});
constructor() {}
// ...
}
这是 UserCreateEditComponent 的 TypeScript 代码:
import { Component, Output } from '@angular/core';
@Component({
selector: 'app-user-create-edit',
templateUrl: './user-create-edit.component.html',
styleUrls: ['./user-create-edit.component.scss']
})
export class UserCreateEditComponent {
@Output() saveModel: EventEmitter<T> = new EventEmitter();
user = new User();
constructor() {}
save() {
this.saveModel.emit(this.user);
}
}
因此,在 ProcutCreateEditComponent 中,我创建了一个 UserCreateEditComponent 实例,并将该实例传递给 DialogPreparatorComponent。 DialogPreparatorComponent 在对话框中显示 UserCreateEditComponent,然后我单击保存按钮(在 UserCreateEditComponent 中),saveModel()
发送到 DialogPreparatorComponent。我想......但我收到的不是这个错误消息:
Event binding saveModel not emitted by any directive on an embedded template.
Make sure that the event name is spelled correctly and all directives are
listed in the "@NgModule.declarations". ("
</div>
<div class="modal-body">
<ng-template #dialogHost [ERROR ->](saveModel)="setModel($event)"></ng-template>
</div>
</div>
其实很好,因为 ng-template
没有 saveModel()
输出。
但是我怎样才能捕捉到 UserCreateEditComponent 的输出呢?
由于您是在代码中创建组件,因此您无法在模板中添加事件处理程序,您必须在创建组件时也将其添加到组件代码中。
例如,您可以更改渲染组件方法以同时添加订阅。
renderComponent() {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dialogContent.component);
this.dialogHost.clear();
const componentRef = this.dialogHost.createComponent(componentFactory);
(componentRef.instance as DialogContentInterface).data = this.dialogContent.data;
(componentRef.instance as DialogContentInterface).saveModel.subscribe((model) => this.setModel(model));
}
希望我正确理解了您的模型,并且您在此处创建的组件具有输出 属性。
确保在销毁动态组件时也取消订阅,这样您就不会因为剩余的订阅而导致内存泄漏。