# 3.2.对话框使用指南

在开发之中，常常会使用到对话框，创建对话框可以通过**服务方式**或**模板方式**创建。本小节中，我们来探讨一下。

## 一、服务方式创建（推荐使用）

### 关注点

使用对话框，人们常常关注的是以下问：

#### 父组件如何传递数据给对话框？

1. 在对话框中使用@Input() 装饰器声明对话框需要的参数
2. 父组件中，通过 nzComponentParams 传入参数

#### 对话框如何返回数据给父组件？

1. 在对话框中编写方法 ，方法返回值为待返回给父组件的数据
2. 在父组件中，通过modal.getContentComponent()或直接使用nzOnOk: (对话框组件)可以获取到对话框组件实例，通过调用步骤1中的方法获取返回值

### 示例

#### 父组件部分

**模板**

src/app/plan/plan.component.html

```markup
<a (click)="createEditModal(data)">更新</a>
```

**控制器**

src/app/plan/plan.component.ts

```typescript
import {Component, OnInit, ViewContainerRef} from '@angular/core';
import {NzModalService} from 'ng-zorro-antd/modal';
import {NzMessageService} from 'ng-zorro-antd';

@Component({
  selector: 'app-plan',
  templateUrl: './plan.component.html',
  styleUrls: ['./plan.component.css']
})
export class PlanComponent implements OnInit {

  constructor(private service: PlanService,
              private modal: NzModalService,
              private message: NzMessageService,
              private viewContainerRef: ViewContainerRef) {
  }


  /**
   * 创建编辑对话框
   */
  createEditModal(plan: any): void {

    const modal = this.modal.create({
      nzTitle: '编辑计划',
      // 对话框的内容，本例中为一个表单组件
      nzContent: PlanUpdateComponent,
      nzViewContainerRef: this.viewContainerRef,
      // 父组件传递给对话框的数据
      nzComponentParams: {
        data: plan,
      },
      nzOnOk: (componentInstance: PlanUpdateComponent) => {
        // 校验表单的正确性，若表单校验通过，则获取表单数据并调用api执行更新操作；否则返回false，不允许提交表单
        const isValid: boolean = componentInstance.isValidForm();
        if (isValid) {
          const result: Plan = componentInstance.getFormData();
          this.updatePlan(result.id, result);
          return result;
        }
        return false;
      }
    });
    // 当对话框开启时，打印日志
    modal.afterOpen.subscribe(() => console.log('[afterOpen] emitted!'));
    // 当对话框关闭时，返回结果
    modal.afterClose.subscribe(result => console.log('[afterClose] The result is:', result));
  }

}
```

#### 对话框部分

**模板**

src/app/plan/plan-update/plan-update.component.html

```markup
<form nz-form [formGroup]="validateForm">

  <nz-form-item>
    <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="name" nzRequired>name</nz-form-label>
    <nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid name!">
      <input nz-input id="name" formControlName="name"/>
    </nz-form-control>
  </nz-form-item>

    ...

</form>
```

**控制器**

src/app/plan/plan-update/plan-update.component.ts

```typescript
import {Component, Input, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {FormUtil} from '../../../utils/form.util';
import {NzModalRef} from 'ng-zorro-antd';

@Component({
  selector: 'app-plan-create',
  templateUrl: './plan-create.component.html',
  styleUrls: ['./plan-create.component.css']
})
export class PlanCreateComponent implements OnInit {

  // 对话框需要的参数，由外部组件传入
  @Input() data?: Plan;

  /**
   * 校验表单
   */
  isValidForm(): boolean {
    return this.formUtil.isValidForm(this.validateForm);
  }

  /**
   * 获取表单数据并进行处理
   */
  getFormData(): Plan {
    const data: Plan = this.formUtil.getFormData(this.data, this.validateForm);
    const times: any[] = this.validateForm.get('rangePicker').value;
    data.startTime = times[0];
    data.expireTime = times[1];
    return data;
  }

}
```

## 二、模板方式创建

### 关注点

使用对话框，人们常常关注的是以下问：

#### 父组件如何传递数据给对话框？对话框如何返回数据给父组件？

1. 在控制器中设置一个变量，作为父模板和对话框模板之间的桥梁
2. 在父模板或对话框模板中，改变变量的值，达到数据传递的作用

### 示例

#### 模板

```markup
<!-- 父模板部分 -->
<button nz-button nzType="primary" (click)="showUpdateModal()">


<!-- 对话框模板部分 -->
<nz-modal
  [(nzVisible)]="isUpdateModalVisible"
  nzTitle="Modal Title"
  (nzOnCancel)="handleUpdateCancel()"
  (nzOnOk)="handleUpdateOk()"
  [nzOkLoading]="isOkUpdateModalLoading>

  <form nz-form [formGroup]="validateForm">

    <nz-form-item>
      <nz-form-label [nzSm]="6" [nzXs]="24" nzFor="name" nzRequired>name</nz-form-label>
      <nz-form-control [nzSm]="14" [nzXs]="24" nzErrorTip="The input is not valid name!">
        <input nz-input id="name1" formControlName="name" [ngModel]="waitingUpdatedData?.name"/>
      </nz-form-control>
    </nz-form-item>

    ...

</nz-modal>
```

#### 控制器

```typescript
import {Component, OnInit, ViewContainerRef} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';

@Component({
  selector: 'app-plan',
  templateUrl: './plan.component.html',
  styleUrls: ['./plan.component.css']
})
export class PlanComponent implements OnInit {

  // 控制对话框是否可见
  isUpdateModalVisible = false;

  // 控制对话框
  isOkUpdateModalLoading = false;

  // 对话框和父组件之间的桥梁，用于传递数据
  waitingUpdatedData: any;

  validateForm!: FormGroup;

  constructor(private fb: FormBuilder) {
  }

  ngOnInit(): void {
    this.validateForm = this.fb.group({
      name: [null, [Validators.required]],
      expectedValue: [null, [Validators.required]],
      rangePicker: [[]],
      description: [null, [Validators.required]],
    });
    ...
  }

  /**
   * 显示对话框
   */
  showUpdateModal(id: string, data: any): void {
    // 初始化对话框所需的数据
    const dateRange: Date[] = [new Date(data.startTime), new Date(data.expireTime)];
    data['dateRange'] = dateRange;
    this.waitingUpdatedData = data;
    console.log(this.waitingUpdatedData)
    // 手动设置对话框可见
    this.isUpdateModalVisible = true;
  }

  /**
   * 表单提交
   */
  submitUpdateForm(): void {
    this.waitingUpdatedData.startTime = this.waitingUpdatedData.dateRange[0];
    this.waitingUpdatedData.expireTime = this.waitingUpdatedData.dateRange[1];
    this.updatePlan(this.waitingUpdatedData.id, this.waitingUpdatedData);
  }

  /**
   * 重置表单
   */
  resetForm(): void {
    this.validateForm.reset();
    for (const key in this.validateForm.controls) {
      this.validateForm.controls[key].markAsPristine();
      this.validateForm.controls[key].updateValueAndValidity();
    }
  }

  /**
   * 处理ok时间
   */
  handleUpdateOk(): void {
    // 显示加载中
    this.isOkUpdateModalLoading = true;
    // 等待1秒，用于模拟请求api所需时间。 1秒后，提交表单数据，手动隐藏对话框，并重置表单
    setTimeout(() => {
      this.submitUpdateForm();
      this.isUpdateModalVisible = false;
      this.isOkUpdateModalLoading = false;
      this.resetForm();
    }, 1000);
  }

  /**
   * 处理取消事件
   */
  handleUpdateCancel(): void {
    // 设置对话框不可见，并重置表单
    this.isUpdateModalVisible = false;
    this.resetForm();
  }

}
```

## 三、对比

相比服务方式，模板方式创建对话框代码不够优雅

* 服务方式创建对话框只需要一个方法，而模板方式却需要1-2个变量、4-5个方法
* 模板方式需要在控制器中，手动通过变量控制对话框的显示和消失
* 模板方式需要手动重置表单


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://moluo.gitbook.io/notes/bian-cheng-xue-xi/qian-duan/angular-da-zao-qi-ye-ji-xie-zuo-ping-tai/3.2.-dui-hua-kuang-shi-yong-zhi-nan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
