Blog
Pass Data to Angular Material Dialogs with MAT_DIALOG_DATA
Use Angular Material MatDialog with MAT_DIALOG_DATA to build reusable, data-driven modal dialogs without duplicating markup.
Published by Tasadduq Burney
04 Nov 2026, 13:12 UTC
4 min78.3K views0

You need a modal that displays different information depending on which button opened it. Copying the same dialog template into every feature module quickly becomes unmaintainable, and hard-coding values inside the dialog makes it impossible to reuse. The practical fix is to treat the dialog as a self-contained component and inject the data it needs at open time.
Angular Material’s MatDialog service opens any Angular component as a modal. By combining MatDialog.open() with the MAT_DIALOG_DATA injection token, the dialog component stays agnostic about who called it. It receives data through Angular’s dependency injection system and returns a result through an observable when it closes. A component factory is Angular’s internal mechanism for creating a component instance dynamically; MatDialog uses it to render the dialog content.
How MatDialog passes data to a modal
MatDialog.open(componentType, config) takes a component class and an optional configuration object. To send data into the dialog, set data in MatDialogConfig. Inside the dialog component, inject MAT_DIALOG_DATA to read that object. This pattern works in Angular Material 12 and later; the examples here assume Angular Material 15.Worked example: a reusable role-assignment dialog
Imagine an admin page where clicking a user row opens a dialog to change that user’s role. The dialog needs the user’s ID and current role, and the parent needs to know the selected role after the user clicks Save. First, generate the dialog component in your project’s terminal:ng generate component dialogs/role-assignment
Run this from the project root. No special permissions are required. The command creates a component folder under src/app/dialogs/role-assignment.
Next, declare the dialog component and import MatDialogModule in the module that opens it. If the dialog is opened from a lazy-loaded feature module, that module must import MatDialogModule itself; otherwise Angular cannot resolve the component factory.
import { NgModule } from '@angular/core';
import { MatDialogModule } from '@angular/material/dialog';
import { RoleAssignmentDialog } from './dialogs/role-assignment/role-assignment.dialog';
@NgModule({
declarations: [RoleAssignmentDialog],
imports: [MatDialogModule]
})
export class AdminModule {}
In Ivy-based Angular applications (version 9 and later), entryComponents is usually unnecessary. The dialog component injects MAT_DIALOG_DATA and exposes a close method that returns the chosen role:
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
@Component({
selector: 'app-role-assignment',
template: `
Assign role to {{ data.userName }}
Role
{{ role }}
Cancel
Save
`
})
export class RoleAssignmentDialog {
selectedRole: string;
roles = ['viewer', 'editor', 'admin'];
constructor(public dialogRef: MatDialogRef,
@Inject(MAT_DIALOG_DATA) public data: { userId: string; userName: string; currentRole: string }) {
this.selectedRole = data.currentRole;
}
onSave(): void {
this.dialogRef.close(this.selectedRole);
}
onCancel(): void {
this.dialogRef.close(undefined);
}
}
From the parent component, open the dialog and subscribe to the result:
import { MatDialog } from '@angular/material/dialog';
import { RoleAssignmentDialog } from '../dialogs/role-assignment/role-assignment.dialog';
@Component({ ... })
export class UserListComponent {
constructor(private dialog: MatDialog) {}
openRoleDialog(user: { id: string; name: string; role: string }): void {
const ref = this.dialog.open(RoleAssignmentDialog, {
data: {
userId: user.id,
userName: user.name,
currentRole: user.role
},
width: '350px'
});
ref.afterClosed().subscribe(result => {
if (result) {
// result is the selected role string
this.updateUserRole(user.id, result);
}
});
}
}
When the user clicks Save, dialogRef.close(this.selectedRole) resolves the observable with the chosen role. Clicking Cancel or the backdrop closes the dialog with undefined, so the parent can ignore the result or treat it as a dismissal.
Trade-offs and practical checks
- Large payloads: Passing a big object through MAT_DIALOG_DATA can trigger extra change detection cycles when the dialog opens. Pass only an identifier and fetch the details inside the dialog if the payload is large.
- Lazy loading: A dialog opened from a lazy-loaded module fails if that module does not import MatDialogModule. Check the browser console for errors about component factory resolution if the dialog does not appear.
- Keyboard navigation: The backdrop traps focus inside the dialog. Native form controls need proper aria-label or aria-labelledby attributes, otherwise screen-reader users may lose context.
Takeaway
Use MatDialog.open() with MAT_DIALOG_DATA when you need a modal that adapts to its caller. Keep the dialog component focused on one task, pass small data objects through the injection token, and handle the returned observable in the parent. This keeps modal logic encapsulated, testable, and reusable across feature modules.0 replies
A thoughtful contribution can make all the difference. Be the first to share one.