对话框 Dialog

dialog 概览

MatDialog 服务可用于打开具有 Material Design 样式和动画效果的模态对话框。

The MatDialog service can be used to open modal dialogs with Material Design styling and animations.

Dialog Overview

通过调用 open 方法并传要加载的组件和可选的配置对象可以打开对话框。 open 方法将返回一个 MatDialogRef 的实例:

A dialog is opened by calling the open method with a component to be loaded and an optional config object. The open method will return an instance of MatDialogRef:

let dialogRef = dialog.open(UserProfileComponent, {
  height: '400px',
  width: '600px',
});

MatDialogRef 提供了已打开对话框的一个引用。可用它来关闭对话框和接受关闭对话框后的通知。 当该对话框关闭时,任何一个通知用的 Observable 都会结束(complete)。

The MatDialogRef provides a handle on the opened dialog. It can be used to close the dialog and to receive notifications when the dialog has been closed. Any notification Observables will complete when the dialog closes.

dialogRef.afterClosed().subscribe(result => {
  console.log(`Dialog result: ${result}`); // Pizza!
});

dialogRef.close('Pizza!');

通过 MatDialog 创建的组件可以注入 MatDialogRef,并用它来关闭包含该组件的对话框。 当关闭时,可以提供一个可选的结果值。该结果值会作为结果转发给 afterClosed 事件。

Components created via MatDialog can inject MatDialogRef and use it to close the dialog in which they are contained. When closing, an optional result value can be provided. This result value is forwarded as the result of the afterClosed Observable.

@Component({/* ... */})
export class YourDialog {
  constructor(public dialogRef: MatDialogRef<YourDialog>) { }

  closeDialog() {
    this.dialogRef.close('Pizza!');
  }
}

You only need to specify entryComponents if your project uses ViewEngine. Projects using Angular Ivy don't need entryComponents.

由于 MatDialog 是在运行时实例化组件的,所以 Angular 编译器需要额外的信息才能为对话框内容组件创建所需的 ComponentFactory

Because MatDialog instantiates components at run-time, the Angular compiler needs extra information to create the necessary ComponentFactory for your dialog content component.

对于任何要加载到对话框中的组件,都必须在 entryComponents 列表中包含你的组件类,以便 Angular 编译器知道如何为它创建 ComponentFactory

For any component loaded into a dialog, you must include your component class in the list of entryComponents in your NgModule definition so that the Angular compiler knows to create the ComponentFactory for it.

@NgModule({
  imports: [
    // ...
    MatDialogModule
  ],

  declarations: [
    AppComponent,
    ExampleDialogComponent
  ],

  entryComponents: [
    ExampleDialogComponent
  ],

  bootstrap: [AppComponent]
})
export class AppModule {}

对话框的默认选项可以通过在应用根模块中为 MAT_DIALOG_DEFAULT_OPTIONS 令牌提供一个 MatDialogConfig 实例来指定。

Default dialog options can be specified by providing an instance of MatDialogConfig for MAT_DIALOG_DEFAULT_OPTIONS in your application's root module.

@NgModule({
  providers: [
    {provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: {hasBackdrop: false}}
  ]
})

如果要和对话框共享数据,可以通过 data 选项把信息传给该组件。

If you want to share data with your dialog, you can use the data option to pass information to the dialog component.

let dialogRef = dialog.open(YourDialog, {
  data: { name: 'austin' },
});

要在对话框组件中访问此数据,可以使用依赖注入令牌 MAT_DIALOG_DATA

To access the data in your dialog component, you have to use the MAT_DIALOG_DATA injection token:

import {Component, Inject} from '@angular/core';
import {MAT_DIALOG_DATA} from '@angular/material/dialog';

@Component({
  selector: 'your-dialog',
  template: 'passed in {{ data.name }}',
})
export class YourDialog {
  constructor(@Inject(MAT_DIALOG_DATA) public data: {name: string}) { }
}

注意,如果你正在使用模板对话框(用 TemplateRef 打开的对话框),其数据在模板中是隐式可用的:

Note that if you're using a template dialog (one that was opened with a TemplateRef), the data will be available implicitly in the template:

<ng-template let-data>
  Hello, {{data.name}}
</ng-template>
Injecting data when opening a dialog

下面几个指令能让你更轻松地定义对话框内容的结构:

Several directives are available to make it easier to structure your dialog content:

名称

Name

说明

Description

mat-dialog-title

[Attr] 对话框标题,应用于标题元素(如 <h1><h2>

[Attr] Dialog title, applied to a heading element (e.g., <h1>, <h2>)

<mat-dialog-content>

对话框中主要的可滚动内容

Primary scrollable content of the dialog.

<mat-dialog-actions>

对话框底部动作按钮的容器

Container for action buttons at the bottom of the dialog. Button alignment can be controlled via the align attribute which can be set to end and center.

mat-dialog-close

[Attr] 添加到 <button> 上,点击它时会用它绑定的值(可选)作为结果来关闭对话框

[Attr] Added to a <button>, makes the button close the dialog with an optional result from the bound value.

例如:

For example:

<h2 mat-dialog-title>Delete all elements?</h2>
<mat-dialog-content>This will delete all elements that are currently on this page and cannot be undone.</mat-dialog-content>
<mat-dialog-actions>
  <button mat-button mat-dialog-close>Cancel</button>
  <!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
  <button mat-button [mat-dialog-close]="true">Delete</button>
</mat-dialog-actions>

一旦打开了对话框,它就会自动把焦点转给第一个可接受焦点的元素。

Once a dialog opens, the dialog will automatically focus the first tabbable element.

你可以通过 tabindex 属性来控制哪个元素可以接受焦点。

You can control which elements are tab stops with the tabindex attribute

<button mat-button tabindex="-1">Not Tabbable</button>
Dialog with header, scrollable content and actions

由于 MatDialog 会在运行期间实例化组件,因此 Angular 编译器需要额外的信息来为对话框的内容组件来创建必要的 ComponentFactory

Because MatDialog instantiates components at run-time, the Angular compiler needs extra information to create the necessary ComponentFactory for your dialog content component.

对于任何加载到对话框中的组件,你都必须把它包含进 NgModule 定义中的 entryComponents 列表中,以便 Angular 编译器了解如何为它创建 ComponentFactory

For any component loaded into a dialog, you must include your component class in the list of entryComponents in your NgModule definition so that the Angular compiler knows to create the ComponentFactory for it.

@NgModule({
  imports: [
    // ...
    MatDialogModule
  ],

  declarations: [
    AppComponent,
    ExampleDialogComponent
  ],

  entryComponents: [
    ExampleDialogComponent
  ],

  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

默认情况下,每个对话框的根元素上都有一个 role="dialog" 属性。当打开对话框时,也可以通过 MatDialogConfig 参数把该角色修改为 alertdialog

By default, each dialog has role="dialog" on the root element. The role can be changed to alertdialog via the MatDialogConfig when opening.

aria-labelaria-labelledbyaria-describedby 属性也同样可以通过 MatDialogConfig 参数进行设置。 通常,每个对话框都会通过 aria-labelaria-labelledby 设置一个标签。

The aria-label, aria-labelledby, and aria-describedby attributes can all be set to the dialog element via the MatDialogConfig as well. Each dialog should typically have a label set via aria-label or aria-labelledby.

当对话框打开时,会把焦点转给它所能知道的第一个可获得焦点的元素。 为了阻止用户 tab 进背景中的元素,Material 对话框使用焦点陷阱来把焦点困在内部。 当对话框关闭时,它就会把焦点还给打开对话框之前拥有焦点的那个元素。

When a dialog is opened, it will move focus to the first focusable element that it can find. In order to prevent users from tabbing into elements in the background, the Material dialog uses a focus trap to contain focus within itself. Once a dialog is closed, it will return focus to the element that was focused before the dialog was opened.

如果你要添加一个不带文本的关闭按钮(比如只有图标的按钮),请确保给它加一个有意义的 aria-label,以便让那些使用相关辅助技术的用户明白它是做什么的。

If you're adding a close button that doesn't have text (e.g. a purely icon-based button), make sure that it has a meaningful aria-label so that users with assistive technology know what it is used for.

默认情况下,当打开时,对话框中第一个可 tab 进去的子元素将会接受焦点。 不过,也可以通过为另一个可获得焦点的元素添加 cdkFocusInitial 元素来配置它。

By default, the first tabbable element within the dialog will receive focus upon open. This can be configured by setting the cdkFocusInitial attribute on another focusable element.

在对话框中的各个元素之间 tab 的时候,将会确保焦点始终位于对话框元素的内部,当到达 tab 序列的末尾时,就会回到该序列的头部。

Tabbing through the elements of the dialog will keep focus inside of the dialog element, wrapping back to the first tabbable element when reaching the end of the tab sequence.

对话框关闭后,会把焦点返还给打开对话框时拥有焦点的元素。但是,在某些情况下,这个先前拥有焦点的元素可能在 DOM 中不再存在,例如菜单项。要想在这种情况下把焦点手动复原到合适的元素,你可以禁用 MatDialogConfig 中的 restoreFocus,并把它传给 open 方法。然后你可以通过订阅 MatDialogRef 上的 afterClosed 事件来手动返还焦点。

Upon closing, the dialog returns focus to the element that had focus when the dialog opened. In some cases, however, this previously focused element no longer exists in the DOM, such as menu items. To manually restore focus to an appropriate element in such cases, you can disable restoreFocus in MatDialogConfig and pass it into the open method. Then you can return focus manually by subscribing to the afterClosed observable on MatDialogRef.

const dialogRef = this.dialog.open(DialogFromMenuExampleDialog, {restoreFocus: false});

// Manually restore focus to the menu trigger since the element that
// opens the dialog won't be in the DOM any more when the dialog closes.
dialogRef.afterClosed().subscribe(() => this.menuTrigger.focus());

默认情况下,按 ESC 键就会关闭底部操作表。虽然也可以通过 disableClose 选项来禁止此行为,不过一般不应这样做,因为它会打破屏幕阅读器用户所期望的交互模式。

By default pressing the escape key will close the dialog. While this behavior can be turned off via the disableClose option, users should generally avoid doing so as it breaks the expected interaction pattern for screen-reader users.