35

Can anyone provide an example of how to dynamically load a component into a Material MatDialog?

What I would like to do is this: I will provide the MatDialog configuration data with a component Type which the dialog would then create an instance of and place inside it's mat-dialog-content area.

It appears I would need to use some combination of ng-template and viewContainerRef, but I do not know how to instantiate the provided component Type and insert into the desired area.

A simple example:

    <h2 mat-dialog-title>MyTitle</h2>
    <mat-dialog-content>
     <---- dynamically loaded component would be inserted here ---->
    </mat-dialog-content>

    <mat-dialog-actions>
      <button mat-button mat-dialog-close>Cancel</button>
      <button mat-button [mat-dialog-close]="true">Save</button>
    </mat-dialog-actions>
Jake Shakesworth
  • 3,335
  • 4
  • 29
  • 43

2 Answers2

76

There are different options:

1) Built-in structural directive ngComponentOutlet

<ng-container *ngComponentOutlet="data.component"></ng-container> 

Example

2) Using angular material cdk. More precisely you can use PortalModule from secondary entry point @angular/cdk/portal

dialog.component.ts

import { ComponentPortal } from '@angular/cdk/portal';

@Component({...})
export class DialogDialog {

  portal: ComponentPortal<any>;

  constructor(...
    @Inject(MAT_DIALOG_DATA) public data: any) { }

  ngOnInit() {
    this.portal = new ComponentPortal(this.data.component);
  }
      

dialog.component.html

<ng-template [cdkPortalOutlet]="portal"></ng-template>

Example

3) Using Angular low-level API

dialog.component.ts

@Component({...})
export class DialogDialog {

  @ViewChild('target', { read: ViewContainerRef }) vcRef: ViewContainerRef;

  componentRef: ComponentRef<any>;

  constructor(
    ...
    private resolver: ComponentFactoryResolver,
    @Inject(MAT_DIALOG_DATA) public data: any) { }

  ngOnInit() {
    const factory = this.resolver.resolveComponentFactory(this.data.component);
    this.componentRef = this.vcRef.createComponent(factory);
  }


  ngOnDestroy() {
    if (this.componentRef) {
      this.componentRef.destroy();
    }
  }  
}

dialog.component.html

<ng-template #target></ng-template>

Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • 1
    Option 3 worked best for me since I needed access to the component instance. Thanks much! – Jake Shakesworth Feb 13 '18 at 03:08
  • @yurzui can we pass a template string to a ComponentPortal ? or does it always have to a ComponentType. – user636525 Apr 26 '18 at 15:00
  • option number 3 really helped me achieving the wanted behavior ! – liron_hazan Sep 16 '18 at 11:30
  • @yurzui These look really useful. Unfortunately, none of the provided examples now run. – Craig Shearer Jun 23 '20 at 08:57
  • 1
    @CraigShearer Sorry for that. I've just updated all examples – yurzui Jun 23 '20 at 09:08
  • @yurzui what about if the content is form and the actions have to be connected with form – Yahya Essam Aug 30 '20 at 14:07
  • 1
    @yurzui I have tried all 3 solutions, and with all 3 the data that is bound to the component that I pass to the dialog show up as "undefined". I'm not sure what Im doing wrong. Any suggestions? – John Oct 02 '20 at 16:57
  • 1
    this not only answers the question but also shows different ways to create dynamic component. Thanks – The.Wolfgang.Grimmer Oct 19 '20 at 07:18
  • 1
    @yurzui can you please share an example on how to pass @@Input values of dynamic component – Prasanth Jan 20 '21 at 05:28
  • I've been trying for hours to pass data to the inserted component with no luck. Any insight would be greatly appreciated. – Louis Jan 29 '21 at 13:09
  • 1
    @Louis Can you please create a simple reproducible example of what you're trying to achieve in stackblitz? – yurzui Jan 29 '21 at 13:36
  • Thanks! Here is one of your examples copied with what I've been trying to do. Without the injector it works as expected. https://stackblitz.com/edit/ang-9-dynamic-modal-content-w-data?file=src/app/dialog.component.ts – Louis Jan 31 '21 at 17:18
  • 1
    @Louis You can take a look at this updated version https://stackblitz.com/edit/ang-9-dynamic-modal-content-w-data-otdb4p?file=src%2Fapp%2Fdialog.component.html – yurzui Jan 31 '21 at 18:57
  • 1
    I totally see what I was doing wrong. If you are ever in Atlanta I will buy you whatever you drink! This is so much easier than I was making it. Thanks! Implemented It Already And It WORKS! – Louis Jan 31 '21 at 20:01
  • Option 3 doesn't work with angular 14. – Gerardo Buenrostro González Aug 16 '22 at 18:58
-4

The actual modal component .ts: (note that the modal component's html is where you will write your code above)

import {Component, Inject, OnInit} from '@angular/core';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';


@Component({
    selector: 'app-modal',
    templateUrl: './modal.component.html',
    styleUrls: ['./modal.component.css']
})
export class ModalComponent implements OnInit {

    constructor(public dialogRef: MatDialogRef<CreateFirmComponent>,
                @Inject(MAT_DIALOG_DATA) public data: any)
                 {
    }

    ngOnInit() {
    }

    onConfirm() {
        this.dialogRef.close(true);
    }

    onCancel(): void {
        this.dialogRef.close(false);
    }

}

And the component from where you call the Modal:

import {Component, OnInit} from '@angular/core';
import {MatDialog} from '@angular/material';
import {ModalComponent} from './modal-component';


@Component({
    selector: 'app-list',
    templateUrl: './list.component.html',
    styleUrls: ['./list.component.css']
})
export class ListComponent implements OnInit {


    constructor(public dialog: MatDialog) {
    }




    openDialog(): void {
        let dialogRef = this.dialog.open(ModalComponent, {
            width: '500px'
        });

        dialogRef.afterClosed().subscribe(result => {
            // result is what you get after you close the Modal
        });
    }

}
filipbarak
  • 1,835
  • 2
  • 17
  • 28
  • I don't believe this answers my problem. The content inside the tags is determined at run time. So, in my mind, your ModalComponent would then need to inspect the MAT_DIALOG_DATA, which will specify the component TYPE to create, creates that component and inserts that created component inside the tags. – Jake Shakesworth Feb 10 '18 at 17:48