【问题标题】:how to pass data from angular material dialog to parent component?如何将数据从角度材料对话框传递到父组件?
【发布时间】:2019-01-19 18:40:28
【问题描述】:

我正在使用 Angular 6,并且我有一个打开对话框的按钮。在我的对话框中,我有一个获取用户数据的表单,然后我有两个按钮来提交和取消。我试图在控制台中显示我的表单数据,但它返回未定义!有什么问题?这是部分代码:

ma​​in.component.ts:

import { Work } from '../../../../classes/work_shift';
import { DialogContentComponent} from './dialog-content/dialog-content.component';
export class WorkShiftsComponent implements OnInit {
 shifts: Work[];
  name: string;
  start: string;
  end: string;
  constructor(public dialog: MatDialog, private shiftService: WorkShiftsService) { }

  ngOnInit() {
  }

  openDialog() {
    const dialogRef = this.dialog.open(DialogContentComponent, {
      width: '640px',
      disableClose: true,
      data: {name: this.name, start: this.start, end: this.end}
    });
    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
      console.log(result);//returns undefined
    });
  }
}

dialogContent.component.html:

    <mat-dialog-content>
  <form class="example-form">
    <div fxLayout="column" fxLayoutAlign="space-around" class="form">
      <div class="input">
        <mat-form-field class="input4">
          <input matInput placeholder="Shift name">
        </mat-form-field>
      </div>
      <div>
        <mat-form-field class="input input2">
          <input matInput placeholder="Start" atp-time-picker>
        </mat-form-field>
        <mat-form-field class="input input2">
          <input matInput placeholder="End" atp-time-picker >
        </mat-form-field>
      </div>
      <br/>
    </div>
  </form>
</mat-dialog-content>
<mat-dialog-actions>
  <button class="mat-button" mat-button (click)="onClose()">Cancel</button>
  <button class="mat-button" mat-button [mat-dialog-close]="data" cdkFocusInitial color="primary">Create</button>
</mat-dialog-actions>

【问题讨论】:

标签: angular typescript angular-material angular6


【解决方案1】:

数据发送到对话框和/或从对话框接收

对话框组件

// HTML
<div [innerHTML]="data"></div>
<button (click)="cancel()">No</button>
<button (click)="confirm()">Yes</button>

// Typescript
export class DialogComponent {

  // receive data from parent using 'MAT_DIALOG_DATA'
  constructor(@Inject(MAT_DIALOG_DATA) public data: string,
    private dialogRef: MatDialogRef<DialogComponent>) { }

  cancel() {
    // closing itself and sending data to parent component
    this.dialogRef.close({ data: 'you cancelled' })
  }

  confirm() {
    // closing itself and sending data to parent component
    this.dialogRef.close({ data: 'you confirmed' })
  }

}

父组件

constructor(private dialog: MatDialog) { }

// method to open dialog
openDialog() {
    let dialogRef = this.dialog.open(DialogComponent, {
      data: `Are you sure you want to delete?`
    })

    dialogRef.afterClosed().subscribe(res => {
      // received data from dialog-component
      console.log(res.data)
    })
}

【讨论】:

  • 这个救了我的命!正是我正在寻找的。谢谢
  • 我用过 afterClosed() 但不认为是一个通用的解决方案,更像是一个边缘案例。当我不想将 Dlg 的更改传回给父组件时,我已经运行了这个问题,但是使用 afterClose() 我无法阻止它。我认为使用 EventEmitter 的更好解决方案
  • @Gabor 我无法理解您的实际问题或要求。无论您的孩子抛出什么信息,即在我的示例代码中,您都会在点击确认按钮时获得“您已确认”,并在点击取消按钮时获得“您已取消”。你可以通过检查它是这个还是那个来处理 afterClosed().sub ...。简单或更好的解决方案。
  • 我认为总的来说这是一个非常好的解决方案,但我在 Dlg 中所做的主要是修改一个对象并将该对象传回给父级。如果您有 2 个不同的功能,例如使用 afterClose() 进行确认和取消,则必须维护初始对象的 2 个不同副本(一个已修改,一个未修改,或者在适当位置修改它们)但是如果我使用 EventEmitter,我可以设法仅在单击确认时发回数据。我认为使用 EventEmitter 会产生更独立的 Dlg 逻辑,因为在父级中我不必检查对象是否更改(如果我得到一个,我只接受)。
【解决方案2】:

我使用 EventEmitter 而不是订阅对话框的保存事件。更多的工作,但更适合解决我的问题。因为大多数情况下你传递了一个你在 Dlg 中修改的对象,也许你不想把修改传回。该解决方案需要某种 Deep Copy 解决方案。

在对话框中:

import { EventEmitter } from '@angular/core';
@Component({...})
export class DlgComponent {
 data: any
 data: deepCopy(data) // some kindof deep copy solution like lodash
 onSave = new EventEmitter();

 ....
 save() {
     this.onSave.emit(this.data)
 }

在父组件中:

import { MatDialog, MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';

@Component({...})
export class ParentComponent {
 data: any
 
 constructor() {
   public dialogRef: MatDialogRef<DlgComponent>,
 }

 openDlg() {
   let dialogConfig = new MatDialogConfig(); 
   const dialogRef = this.dialog.open(DlgmComponent, dialogConfig);

   dialogRef.componentInstance.onSave.subscribe(data=> {
     this.data = data    
   })
 }
  
}

【讨论】:

  • 当您不想在将数据传递给父级时关闭对话框时,此解决方案会更好。
【解决方案3】:

查看完整教程Link

只需在 close() 方法中将数据从 Dialog 组件传回父组件

//dialog-box.component.ts
import { Component, Inject, Optional } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';

export interface UsersData {
  name: string;
  id: number;
}


@Component({
  selector: 'app-dialog-box',
  templateUrl: './dialog-box.component.html',
  styleUrls: ['./dialog-box.component.css']
})
export class DialogBoxComponent {

  action:string;
  local_data:any;

  constructor(
    public dialogRef: MatDialogRef<DialogBoxComponent>,
    //@Optional() is used to prevent error if no data is passed
    @Optional() @Inject(MAT_DIALOG_DATA) public data: UsersData) {
    console.log(data);
    this.local_data = {...data};
    this.action = this.local_data.action;
  }

  doAction(){
    this.dialogRef.close({event:this.action,data:this.local_data});
  }

  closeDialog(){
    this.dialogRef.close({event:'Cancel'});
  }

}

然后在父组件中获取事件和数据对象/值

//app.component.ts
import { Component, ViewChild } from '@angular/core';

import { MatDialog, MatTable } from '@angular/material';
import { DialogBoxComponent } from './dialog-box/dialog-box.component';

export interface UsersData {
  name: string;
  id: number;
}

const ELEMENT_DATA: UsersData[] = [
  {id: 1560608769632, name: 'Artificial Intelligence'},
  {id: 1560608796014, name: 'Machine Learning'},
  {id: 1560608787815, name: 'Robotic Process Automation'},
  {id: 1560608805101, name: 'Blockchain'}
];
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  displayedColumns: string[] = ['id', 'name', 'action'];
  dataSource = ELEMENT_DATA;

  @ViewChild(MatTable,{static:true}) table: MatTable<any>;

  constructor(public dialog: MatDialog) {}

  openDialog(action,obj) {
    obj.action = action;
    const dialogRef = this.dialog.open(DialogBoxComponent, {
      width: '250px',
      data:obj
    });

    dialogRef.afterClosed().subscribe(result => {
      if(result.event == 'Add'){
        this.addRowData(result.data);
      }else if(result.event == 'Update'){
        this.updateRowData(result.data);
      }else if(result.event == 'Delete'){
        this.deleteRowData(result.data);
      }
    });
  }

  addRowData(row_obj){
    var d = new Date();
    this.dataSource.push({
      id:d.getTime(),
      name:row_obj.name
    });
    this.table.renderRows();

  }
  updateRowData(row_obj){
    this.dataSource = this.dataSource.filter((value,key)=>{
      if(value.id == row_obj.id){
        value.name = row_obj.name;
      }
      return true;
    });
  }
  deleteRowData(row_obj){
    this.dataSource = this.dataSource.filter((value,key)=>{
      return value.id != row_obj.id;
    });
  }


}

【讨论】:

  • 如果您使用表单组并且不能使用 ngModel,您将如何做到这一点
【解决方案4】:

DEMO COMMON POP-FORM

common-pop-service:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

import { MatDialogRef, MatDialog, MatDialogConfig } from '@angular/material';
import { PupupFormComponent } from './pupup-form/pupup-form.component'

@Injectable()
export class CommonModelService {
  animal: string;
  name: string;
  date1: any;
  date2: any
  constructor(public dialog: MatDialog) { }
  openDialog(): Observable<any> {
    const dialogRef = this.dialog.open(PupupFormComponent, {
      width: '250px',
      data: { name: this.name, animal: this.animal, date1: this.date1, date2: this.date2 }
    });

    return dialogRef.afterClosed();
  }
}

parent.component.ts:

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

import { CommonModelService } from './common-model.service'

export interface DialogData {
  animal: string;
  name: string;
}

@Component({
  selector: 'dialog-overview-example',
  templateUrl: 'dialog-overview-example.html',
  styleUrls: ['dialog-overview-example.css'],
})
export class DialogOverviewExample {

  animal: string;
  name: string;

  constructor(private commModel: CommonModelService) { }

  openDialog() {
    this.commModel.openDialog().subscribe(data => {
      console.log(data);
    });
  }
}

parent.component.html:

<button mat-raised-button (click)="openDialog()">Open Form</button>

pup-up-form.html:

<div mat-dialog-content>
    <p>What's your favorite animal?</p>
    <mat-form-field>
        <input matInput [(ngModel)]="data.animal">
    </mat-form-field>

    <mat-form-field>
        <input matInput type="time" atp-time-picker [(ngModel)]="data.date1">
    </mat-form-field>

    <mat-form-field>
        <input matInput type="time" atp-time-picker [(ngModel)]="data.date2">
    </mat-form-field>
</div>

<div mat-dialog-actions>
    <button mat-button (click)="onNoClick()">No Thanks</button>
    <button mat-button [mat-dialog-close]="data" cdkFocusInitial>Ok</button>
</div>

【讨论】:

  • 我用过这个,但我不知道在哪里以及如何在控制台中显示数据!我在单独的 .ts 文件中有两个组件
  • 查看演示。您需要在父组件中订阅数据。
  • 我想我忘了使用 ngModel!我使用它并且名称输入正在工作,但其他人是时间选择器并返回未定义或 NAN!你知道问题出在哪里吗?
  • 让我用不同的 ts 给你完整的例子。
  • 之前的回答没问题,我的名字输入也不错,但是我有两个带有日期选择器指令的输入,只有这两个输入返回 undefined
猜你喜欢
  • 2016-12-30
  • 2019-05-14
  • 2021-11-02
  • 2018-06-17
  • 2020-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多