How to use the MdlDialogService with Angular 2 Material Design Lite (angular2-mdl)

angular 2 Dialogs Material Design Lite

Version 2.0 of Angular 2 Material Design Lite (demo github) comes with a complete rewritten dialog service. This service let’s you create material design lite dialogs from your TypeScript code or right in your templates. It doesn’t requires any additional polyfills.

The Mdl Dialogs

mdl-dialog

TL;DR

Insert the dialog-outlet element as the last child of the body element and start using the imperative and declarative dialogs.

<html>
  <head>...</head>
  <body>
    <app-root>...</app-root>
    <dialog-outlet></dialog-outlet>
  </body>
</html>

Prerequisites

The dialog implementation did not touch the dom directly (for example document.body.appendChild or something like this)! To got this working every dialog needs an instance of a ViewContainerRef where the dialog html can be attached to. There are different ways how you can provide an instance of a ViewContainerRef:

  • place the component dialog-outlet next to the body element of your html file (as seen above)
  • place the component dialog-outlet anywhere as a child of your root-app element. But be aware that the dialogs and the dialog backdrop may not cover your whole html page and the z-index may not work.
  • If you already have an instance of a ViewContainerRef you can inject the MdlDialogOutletService and call setDefaultViewContainerRef to tell the dialogs where they should be attached.

If you are using the showCustomDialog function of the MdlDialogService make sure that the Component you are using is defined as an EntryComponent. This means it must be part of the entryComponents property of an NgModule

Imperative Usage

Imperative usage means that you are using the MdlDialogServiceand create your dialogs from your TypeScript code. There are some built-in Dialogs which can be used by a simple API:

Alert

let result = this.dialogService.alert('This is a simple Alert');
result.subscribe( () => console.log('alert closed') );

The complete function signature is:

alert(alertMessage: string, okText = 'Ok', title?: string): Observable<void>

An Alert is always displayed as a modal dialog. The alertMessage can be a simple text or html. As you can see the title is optional.

Confirm

let result = this.dialogService.confirm('Would you like a mug of coffee?', 'No', 'Yes');
    result.subscribe( () => {
        console.log('confirmed');
      },
      (err: any) => {
        console.log('declined');
      }
    );

The complete function signature is:

  public confirm(question: string, declineText = 'Cancel', confirmText = 'Ok'): Observable<void>

A Confirmis always displayed as a modal dialog. The question can be a simple text or html.

Dialog with actions

The Alert and Confirm dialog are just a special case of a more general prebuilt dialog:

let pDialog = this.dialogService.showDialog({
      title: 'Your choice?',
      message: 'What drink do you prefer to your meal?',
      actions: [
        {
          handler: () => { console.log('Coke'); },
          text: 'One Coke' ,
          isClosingAction: true
        },
        {
          handler: () => { console.log('Vine'); },
          text: 'A bottle of vine'
        },
        {
          handler: () => { console.log('Beer'); },
          text: 'A pint of beer'
        }
      ],
      fullWidthAction: true,
      isModal: false
    });
    pDialog.subscribe( (dialogReference) => console.log('dialog visible', dialogReference) );

The complete function signature is:

 showDialog(config: IMdlSimpleDialogConfiguration): Observable<MdlDialogReference> 

The parameter of the showDialog function is an instance of the IMdlSimpleDialogConfiguration interface. If the dialog is shown the Observable get fired with a reference to the dialog. You can use this reference to hide the dialog or to be informed if the dialog has been closed.

Custom Dialog

The Custom Dialog is the most powerful version of a dialog. angular2-mdl is responsible for showing and hiding the dialog, the backdrop and runs the animations. The whole content of the dialog is up to you. To create a Custom Dialog you need to provide an angular Component. The only requirement for such a Component is that the Component is defined as EntryComponent - e.g. you put the Component in the entryComponents property of an NgModule. Suppose we want to create a LoginComponent that is defined in a reusable LoginModule module:

@NgModule({
  imports: [MdlModule, CommonModule, ReactiveFormsModule],
  declarations: [LoginDialogComponent],
  entryComponents: [LoginDialogComponent],
  providers: [LoginService]
})
export class LoginModule {}

In this case you add your dialog Component in the declarations property and in the entryComponents property. (The LoginService is used for the business logic.)

The LoginComponent looks as follow (a little bit shortened):

@Component({
  selector: 'login-dialog',
  templateUrl: 'login-dialog.component.html'
})
export class LoginDialogComponent implements OnInit {
  public form: FormGroup;
  public username = new FormControl('',  Validators.required);
  public password = new FormControl('', Validators.required);

  constructor(
    private dialog: MdlDialogReference,
    private fb: FormBuilder,
    private loginService: LoginService,
    @Inject( TEST_VALUE) testValue: string) {
  }

  public ngOnInit() {
    this.form = this.fb.group({
      'username':  this.username,
      'password':   this.password
    });
  }

  public login() {
    // do the business logic
  }

  @HostListener('keydown.esc')
  public onEsc(): void {
    this.dialog.hide();
  }
}

The html looks as follow:

<form [formGroup]="form">

  <h3 class="mdl-dialog__title">App Login</h3>

  <div class="mdl-dialog__content">
    <mdl-textfield  type="text" label="Username" formControlName="username" floating-label></mdl-textfield>
    <br/>
    <mdl-textfield type="password" label="Password" formControlName="password" floating-label></mdl-textfield>
  </div>

  <div class="mdl-dialog__actions">
    <button
      type="button"
      mdl-button (click)="login()"
      [disabled]="!form.valid"
      mdl-button-type="raised"
      mdl-colored="primary" mdl-ripple>Login</button>
  </div>
</form>

As you can see there are no interface the LoginDialogComponent must implement or any other dependencies. It’s just a plain angular Component. If this is all done you can show your LoginDialog:

let pDialog = this.dialogService.showCustomDialog({
   component: LoginDialogComponent,
   providers: [{provide: TEST_VALUE, useValue: 'Just an example'}],
   isModal: true
});
pDialog.subscribe( (dialogReference: MdlDialogReference) => {
   console.log('dialog visible', dialogReference);
});

As you can see, it is possible to define an array of providers. These providers will be injected in the CustomDialog-Component. An instance of the MdlDialogReference is also injected into the Component. With this MdlDialogReference you are able to subscribe to a close-event or call hide from within the dialog.

Declarative Usage

If you don’t want to write TypeScript code to show dialogs you can use the declarative version - e.g mdl-alert and mdl-dialog.

mdl-alert

The mdl-dialog component let’s you define an alert within your html templates:

<button
  mdl-button mdl-button-type="raised" mdl-colored="accent"
  mdl-ripple (click)="alert.show()">Show Alert</button>

<mdl-alert
  #alert="mdlAlert"
  message="This is a <em class='mdl-color-text--primary'>simple</em> Alert"
  okText="Got it!"
  (confirmed)="alertConfirmd()"></mdl-alert>

If the user clicks the Got it! button teh event confirmed fires.

mdl-dialog

The mld-dialog component let you define a custom dialog in your html. This is somewhat like the Custom Dialog from above. But you don’t need to create a Component:

<button
    mdl-button mdl-button-type="raised" mdl-colored="primary"
    mdl-ripple (click)="editUserDialog.show()">Edit User Dialog</button>

<mdl-dialog #editUserDialog [mdl-modal]="false" (show)="onDialogShow($event)" (hide)="onDialogHide()">
  <h3 class="mdl-dialog__title">Edit User</h3>
  <div class="mdl-dialog__content">
    <mdl-textfield type="text" label="Username" [(ngModel)]="editedUsername" floating-label autofocus></mdl-textfield>
  </div>
  <div class="mdl-dialog__actions">
    <button mdl-button (click)="saveUser()" mdl-button-type="raised" mdl-colored="primary" mdl-ripple>Save</button>
    <button mdl-button (click)="editUserDialog.close()" mdl-button-type="raised" mdl-ripple>Cancel</button>
  </div>
</mdl-dialog>

The mdl-dialogcomponent can be modal or not modal (mdl-modal attribute) and fires the events showand hideif the dialog get’s visible or hidden.

If you are missing some feature just create a github issue.