1

I am trying to create a component with a dynamic child Component as given in this example: Angular Dynamic Component Loader. Well, it is kind easy, and I have implemented it. In my current case, I am trying to load a component dynamically in MatDialog class. Reference: Angular Material Dialog. So far, so good. My Dynamic Component Class:

@Component({
    selector: 'dynamic-dialog',
    template: `<div class="ad-banner">
            <h3>{{title}}</h3>
            <ng-template dialog-host></ng-template>
          </div>`
    })
export class DialogComponent<T>
{

@ViewChild(DialogDirective) dialogHost: DialogDirective;
title:string;

constructor(
     public dialogRef: MatDialogRef<T>, @Inject(MAT_DIALOG_DATA) public                     
     data: any, component:T,
     private _toast: Toaster, private apiBase: apiBase, type: { new():                 
     T;},
     private componentFactoryResolver: ComponentFactoryResolver
    ){
        let componentFactory = this.componentFactoryResolver.resolveComponentFactory<Component>(type);
        let viewContainerRef = this.dialogHost.viewContainerRef;
        viewContainerRef.clear();
        let componentRef = viewContainerRef.createComponent(componentFactory);
        this.title = data.title;
  }
}

My Dialog Service:

openGenericDialog<T>(t:T, data:{},
        type: { new(): T ;},
        height:string,
        width:string,
        callback
        ): MatDialogRef<DialogComponent<T>> {
    let dialogRef = this.dialog.open(DialogComponent, {
                width: width,
                height:height,
                data: data
            });

     dialogRef.afterClosed().subscribe(result => {
        //when you close the box, it will return the control right here
        if(callback){
            callback(result);
        }
        //write the closed event right here. 
    });

    return dialogRef;
}

Now, it is obvious that if we load any component dynamically in a dialog, it will have a button to close the dialog. Now, I understand that I can use EventEmitter for this to close it. I declare an event emitter, and then try to create an event handler at my generic class. But that will require me to write an OutPut event in each component I may be showing inside that dialog box. Is there a way, where I can directly capture the event in child class? I am not really good with Angular yet, so unable to find the correct way to describe or implement this. All I want is a function, which I can call inside my ChildComponent which will call the close function in the dialog service. Is that intuitive enough? If not, please let me know so I can add more detail. Sorry if it is confusing. Please note that above is not a compilable code and is still WIP, it has been posted here just for the reference to show what I am trying to do.

Update: I fixed it using the injected service. Earlier, I was not comfortable injecting a service because there can be multiple dialogs, one on top of another which could have messed up the references of the active dialogbox. So I did a simple workaround and added a dictionary with a key which will contain the reference of the dialogbox. This way, I will be able to control the dialogboxes using a particular key. Following are the changes I did:

Dialog Service:

@Injectable()
export class DialogService{

dialogs:any={};

constructor(public dialog: MatDialog) {}

Storing a reference in the dictionary:

openGenericDialog<T>(t:T, data:{},
        type: { new(): T ;},
        height:string,
        width:string,
        key:string,  <=== added a unique key to be associated with the dialog 
        callback
        ): MatDialogRef<DialogComponent<T>> {
    let dialogRef = this.dialog.open(DialogComponent, {
                width: width,
                height:height,
                data: data
            });
            //add it in the dictionary
            this.dialogs[key] = dialogRef; <== storing the object in a dictionary using the unique key. 

     dialogRef.afterClosed().subscribe(result => {
        //when you close the box, it will return the control right here

        //remove from the dialog collection

        if(this.dialogs[key]){
            this.dialogs[key] = '';
        }

        if(callback){
            callback(result);
        }
        //write the closed event right here. 
    });

    return dialogRef;
}

Closing the box:

closeDialog(key:string, data:any){
    let dialogRef = this.dialogs[key];

    if(dialogRef){
        dialogRef.close(data);
    }
}

Sweet! Thanks for the help :)

Oo-_-oO
  • 417
  • 7
  • 24

1 Answers1

2

You can either provide a service in the parent component an inject it in the dynamically added child to communicate.

Alternatively you can emit DOM events in the child and listen to them in the parent, for example using @HostListener('my-custom-event', '[$event]‘) onMyCustomEvent(event) {...}

For more details about how to fire custom events see

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • thanks. I added the solution I decided to use. Can you please have a quick look and let me know if this is the right approach. I think it is, because Angular is very much client based and I am assuming it will work like a charm within the same tab of a browser. What do you think? – Oo-_-oO Oct 22 '17 at 11:47
  • I think the approach is fine. I'm not sue what "multiple dialogs" means exactly in the context of your application. Perhaps you can have a different dialog service for each dialog, but that depends how you design your application and what exactly you try to accomplish. – Günter Zöchbauer Oct 22 '17 at 11:51
  • well you got the point somewhat, because I will either need to use separate service for each dialog, which I don't want, because I really want to keep it fully generic, or I will need a workaround which I can make to use single dialog service. Let's say I have a dialog, and couple buttons, one of the buttons opens another dialog. With single service, it will be difficult to get the reference of one dialog, that is why used keys. So I can find the dialog object, as and when required. Makes sense? – Oo-_-oO Oct 22 '17 at 13:35
  • Not really :D, but I don't know enough about the architecture of your app, but as I said, the approach looks fine. If it works for you, I don't see anything wrong with this approach. – Günter Zöchbauer Oct 22 '17 at 13:49