I want to catch the output from the ViewChild
rendered component. The ViewChild's conent shown after an ngIf
fires.
This is my template's code:
<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>
Here is the TypeScript file:
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
}
}
Here is the ProcutCreateEditComponent:
<app-dialog-preparator [dialogContent]="userDialogContent"></app-dialog-preparator>
The TypeScript code for this component:
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() {}
// ...
}
And here is the UserCreateEditComponent's TypeScript code:
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);
}
}
So in the ProcutCreateEditComponent I create an instance of UserCreateEditComponent and pass this instance to the DialogPreparatorComponent. The DialogPreparatorComponent shows the UserCreateEditComponent in a dialog, and then I click on the save button (in the UserCreateEditComponent) there the saveModel()
emitting to the DialogPreparatorComponent. I think... but instead of this I got this error message:
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>
Actualy it is good, because the ng-template
don't have saveModel()
output.
But how can I catch the UserCreateEditComponent's output in that point?
Since you create the component in code you cannot add an event handler in the template you have to add it also in your component code when you create the component.
For example you can change the render component method to also add the subscription.
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));
}
I hope I understood your model correctly and the component that you create here is the one that has the output property.
Make sure to also unsubscribe when the dynamic component will be destroyed so you will not get memory leaks because of remaining subscriptions.