Live demo
https://stackblitz.com/github/maag070208/NgDynamicDialog
Agenda
Angular installation
Create a angular project with specific version
Installation of angular material
Primeflex installation
Create dialog atom component
Create dialog service
Create the components to be called from the dynamic dialog
Try dynamic dialog component
Install angular
npm install -g @angular/cli
Create a angular 15 project
npx @angular/cli@15 new NgDynamicDialog
Install angular material
Step 1: Run installation command
ng add @angular/material
Step 2: Choose angular material theme
Step 3: Set up typography styles
Step 4: Include and enable animations
Install primeflex
npm i primeflex
Import styles on styles.scss
@import 'primeflex/primeflex.scss';
Create Dialog atom component
ng g c components/atoms/dialog --standalone
Step 1: In dialog.component.ts
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ComponentRef,
Inject,
OnDestroy,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-dialog',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule],
templateUrl: './dialog.component.html',
styleUrls: ['./dialog.component.scss'],
})
export class DialogComponent implements OnDestroy, AfterViewInit {
//this is the target element where component dynamically will be added
@ViewChild('target', { read: ViewContainerRef }) vcRef!: ViewContainerRef;
//this will hold the component reference
private componentRef!: ComponentRef<any>;
constructor(
private dialogRef: MatDialogRef<DialogComponent>,
private cdref: ChangeDetectorRef,
@Inject(MAT_DIALOG_DATA)
public data: {
component: any;
data: any;
}
) {}
ngAfterViewInit() {
//create component dynamically
this.componentRef = this.vcRef.createComponent(this.data.component);
//pass some data to component
this.componentRef.instance.initComponent({ ...this.data.data });
//subscribe to the event emitter of component
this.componentRef.instance.onSubmit.subscribe((data: any) => {
this.dialogRef.close(data);
});
//detect changes
this.cdref.detectChanges();
}
ngOnDestroy() {
if (this.componentRef) {
this.componentRef.destroy();
}
}
}
Step 2: In dialog.component.html
<ng-template #target></ng-template>
Create Dialog service
Step 1: Run command
ng g s core/services/dialog
Step 2: In dialog.service.ts
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { DialogComponent } from 'src/app/components/atoms/dialog/dialog.component';
@Injectable({
providedIn: 'root'
})
export class DialogService {
constructor(private dialog: MatDialog) {}
public showDialog<T>(dynamicComponent: any, data?: T): any {
return this.dialog.open(DialogComponent, {
data: {
component: dynamicComponent,
data: data,
},
});
}
}
Step 3: Remember add “MatDialogModule” in app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatButtonModule,
MatDialogModule, //<---- import
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Create the components to be called from the dynamic dialog
Step 1: Create a models folder in path “app/core/models”
Step 2: Add in models folder the dialog.ts file
import { EventEmitter } from "@angular/core";
export interface DynamicDialogComponent<T> {
//the initComponent method will be called when component is created
initComponent(data: any): void;
//the onSubmit event will be emitted when user clicks on submit button
onSubmit: EventEmitter<T>;
}
Step 3: Add in models folder the user.ts file
export interface IUser {
name: string;
phone: string;
email: string;
}
Step 4: Create Form1 component
ng g c components/molecules/form1 --standalone
Step 5: In form1.component.ts file
import { Component, EventEmitter, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatInputModule } from '@angular/material/input';
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { DynamicDialogComponent } from 'src/app/core/models/dialog';
import { IUser } from 'src/app/core/models/user';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'app-form1',
standalone: true,
imports: [CommonModule, MatInputModule,MatButtonModule, ReactiveFormsModule],
templateUrl: './form1.component.html',
styleUrls: ['./form1.component.scss'],
})
export class Form1Component implements DynamicDialogComponent<IUser> {
//this is the output event that you need to emit when the form is submitted
@Output() onSubmit: EventEmitter<IUser> = new EventEmitter<IUser>();
//this is the form that you need to create
public form1!: FormGroup;
//this is the user that you need to pass to the form
public user!: IUser;
//this method replace the ngOnInit() method
initComponent(data: IUser): void {
//this is the data that you pass from the parent component
this.user = data;
//this is the method that initialize the form
this.initForm();
}
//if you have a form, you need to create a method to initialize it
initForm(): void {
this.form1 = new FormGroup({
name: new FormControl(this.user.name, Validators.required),
phone: new FormControl(this.user.phone, Validators.required),
email: new FormControl(this.user.email, Validators.required),
});
}
}
Step 6: In form1.component.html file
<form [formGroup]="form1" class="flex flex-column m-5">
<mat-form-field appearance="outline">
<mat-label>Name</mat-label>
<input matInput formControlName="name" placeholder="maag" />
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Phone</mat-label>
<input matInput formControlName="phone" placeholder="1231231231" />
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Email</mat-label>
<input matInput formControlName="email" placeholder="maag@gmail.com" />
</mat-form-field>
<button
mat-raised-button
color="primary"
[disabled]="form1.invalid"
(click)="onSubmit.emit(form1.value)"
>
Submit
</button>
</form>
Step 7: Create a delete dialog component
ng g c components/molecules/delete-dialog --standalone
Step 8: In delete-dialog.component.ts file
import { Component, EventEmitter, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DynamicDialogComponent } from 'src/app/core/models/dialog';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'app-delete-dialog',
standalone: true,
imports: [CommonModule, MatButtonModule],
templateUrl: './delete-dialog.component.html',
styleUrls: ['./delete-dialog.component.scss']
})
export class DeleteDialogComponent implements DynamicDialogComponent<boolean> {
@Output() onSubmit: EventEmitter<boolean> = new EventEmitter<boolean>();
initComponent(): void {}
}
Step 9: In delete-dialog.component.ts file
<section class="flex flex-column">
<p class="text-center">Are you shure to delete this register?</p>
<div class="flex justify-content-between">
<button mat-raised-button
color="warn"
(click)="onSubmit.emit(true)">Delete</button>
<button mat-raised-button
color="accent"
(click)="onSubmit.emit(false)">
Cancel
</button>
</div>
</section>
Try dynamic dialog component
Step 1: in app.component.ts file
import { Component } from '@angular/core';
import { DialogService } from './core/services/dialog.service';
import { Form1Component } from './components/molecules/form1/form1.component';
import { IUser } from './core/models/user';
import { DeleteDialogComponent } from './components/molecules/delete-dialog/delete-dialog.component';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
constructor(private _dialogService: DialogService) {}
public async showForm1(): Promise<void> {
const dialogRef = this._dialogService.showDialog<IUser>(
Form1Component,
{} as IUser
);
const result = await dialogRef.afterClosed().toPromise();
console.log(result);
}
public async showForm1WithInitialData(): Promise<void> {
const user: IUser = {
name: 'maag',
phone: '1234567890',
email: 'maag070208@gmail.com',
};
const dialogRef = this._dialogService.showDialog<IUser>(
Form1Component,
user
);
const result = await dialogRef.afterClosed().toPromise();
console.log(result);
}
public async showDeleteDialog(): Promise<void> {
const dialogRef = this._dialogService.showDialog<boolean>(
DeleteDialogComponent
);
const result = await dialogRef.afterClosed().toPromise();
console.log(result);
}
}
Step 1: in app.component.html file
<div class="flex flex-column gap-4 m-4">
<button mat-raised-button (click)="showForm1()">Show Form 1</button>
<button mat-raised-button color="primary" (click)="showForm1WithInitialData()">Show Form 1 With Initial Data</button>
<button mat-raised-button color="accent" (click)="showDeleteDialog()">Show confirm dialog</button>
</div>
Top comments (0)