Brian Love
Angular + TypeScript Developer in Denver, CO

Passing Data with MdDialog

Reading time ~3 minutes

Make your Material MdDialog stateless by passing data as part of the MdDialogConfig object.

[Update April 11, 2017]

With the release of @angular/material version 2.0.0-beta.3 the config property of MdDialogRef has been removed. We now use the MD_DIALOG_DATA token to inject the data into our dialog’s component. See this GitHub issue.

Stateless

What do I mean by stateless? Let’s look at the definition of stateless from Wikipedia:

Of a system or protocol, such that it does not keep a persistent state between transactions.

In a basic sense, a component in our Angular application is considered stateful when it:

  • is dependent on requesting data from a service,
  • has knowledge about the state of the application,
  • resolves data via routing, or
  • changes the state of our application.

And, a component might be considered stateless when it:

  • receives data via input properties,
  • does not request data from a service,
  • emits data to a parent stateful component that acts upon it, or
  • renders further stateless components.

Stateless components are sometimes referred to as dumb components.

Want to read more about stateless vs. stateful components? Check out Todd Motto’s article on Stateful and Stateless components.

Passing data in MdDialogConfig

First, let’s take a look at the MdDialogConfig interface:

/**
 * Configuration for opening a modal dialog with the MdDialog service.
 */
export declare class MdDialogConfig {
    viewContainerRef?: ViewContainerRef;
    /** The ARIA role of the dialog element. */
    role?: DialogRole;
    /** Whether the user can use escape or clicking outside to close a modal. */
    disableClose?: boolean;
    /** Width of the dialog. */
    width?: string;
    /** Height of the dialog. */
    height?: string;
    /** Position overrides. */
    position?: DialogPosition;
    /** Data being injected into the child component. */
    data?: any;
}

Note the data property. It allows us to inject data into the child component that is created when we open() an MdDialog. Here is an example component that opens the dialog and passes a User model object to the child component:

import { Component, Input } from "@angular/core";
import { MdDialog } from "@angular/material";

//dialog
import { InviteDialogComponent } from "../dialogs/invite-dialog/invite-dialog.component";

//model
import { User } from "../models/user";

/**
 * The invite card.
 * @class InviteCardComponent
 */
@Component({
  selector: "j11-invite-card",
  templateUrl: "./invite-card.component.html",
  styleUrls: ["./invite-card.component.scss"]
})
export class InviteCardComponent {

  //the authenticated user
  @Input() public user: User;

  /**
   * @constructor
   * @param {MdDialog} mdDialog
   */
  constructor(private mdDialog: MdDialog) { }

  /**
   * Open the dialog to send an invite
   * @method openInviteDialog
   */
  public openInviteDialog() {
    this.mdDialog.open(InviteDialogComponent, {
      disableClose: true,
      data: {
        user: this.user
      }
    });
  }
}

Let’s review the InviteCardComponent:

  • We import MdDialog from the @angular/material package.
  • This component itself is stateless and receives the user via property binding.
  • We inject an instance of MdDialog in the constructor function.
  • The openInviteDialog() method is invoked by the UI to open the InviteDialogComponent dialog.
  • The data property is an object that contains our custom data that will be passed to the child dialog component.

Accessing custom Data

First, we need to import the MD_DIALOG_DATA constant, which is really just an instance of the OpaqueToken class:

import { MdDialogRef, MD_DIALOG_DATA } from "@angular/material";

Next, use the @Inject() decorator to inject the data via our constructor() function:

constructor(@Inject(MD_DIALOG_DATA) private data: { user: User })

Now we just pick that data up inside our child component’s ngOnInit() method. Here is the full example:

import { Component, Inject, OnInit } from "@angular/core";
import { MdDialogRef, MD_DIALOG_DATA } from "@angular/material";

//model
import { User } from "../models/user";

@Component({
  selector: "j11-invite-dialog",
  templateUrl: "./invite-dialog.component.html",
  styleUrls: ["./invite-dialog.component.scss"]
})
export class InviteDialogComponent implements OnInit {

  //authenticated user
  public user: User;

  /**
   * @constructor
   * @param {Object} data
   * @param {MdDialogRef<InviteDialogComponent>} mdDialogRef
   */
  constructor(
    @Inject(MD_DIALOG_DATA) private data: { user: User },
    private mdDialogRef: MdDialogRef<InviteDialogComponent>
  ) { }

  /**
   * Let's do this.
   * @method ngOnInit
   */
  public ngOnInit() {
    //set custom data from parent component
    this.user = this.data.user;
  }
}

OK, let’s review the code above:

  • First, I am importing the Inject decorator.
  • Then, I am importing the MdDialogRef class and the MD_DIALOG_DATA constant.
  • I have declared a publicly accessible variable named user that is the authenticated user in my app.
  • The data is injected in the constructor function using the @Inject decorator.
  • Finally, in the ngOnInit() method I am setting the reference of user.

That’s it. Super easy. Thanks to the @angular/material2 team!

Brian Love

Hi, I'm Brian. I am interested in TypeScript, Angular and Node.js. I'm married to my best friend Bonnie, I live in Denver and I ski (a lot).