77

I'm using angular 6 and I have a button which opens a dialog. in my dialog, I have a form that gets user's data and then I have two buttons to submit and cancel. I tried to show my form's data in the console but it returns undefined! whats the problem? here is part of codes:

main.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>
fariba.j
  • 1,737
  • 7
  • 23
  • 42
  • `I have a form that gets user's data` I don't see where your form gets user's data. In this example form gets user's data https://stackblitz.com/edit/angular-s3kjvx-l7cgyw?file=app/dialog-overview-example-dialog.html – yurzui Aug 13 '18 at 05:12

6 Answers6

75

Check full Tutorial Link

Just pass data back from Dialog component to parent in close() method

enter image description here

//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'});
  }

}

Then get event & data objects/values in parent component back

//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;
    });
  }


}
Code Spy
  • 9,626
  • 4
  • 66
  • 46
52

Data send to and/or receive from Dialog

Dialog Component

// 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' })
  }

}

Parent Component

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)
    })
}
WasiF
  • 26,101
  • 16
  • 120
  • 128
  • 2
    This one saved my life! Exactly what I was searching. Thanks – Shahriar Rahman Zahin Dec 21 '20 at 12:50
  • I have used afterClosed() but don't think a general solution, more like an edge case. I have run the issue when I do not want to pass back the changes of the Dlg to the parent Componenent, but with afterClose() I cannot prevent it. I think a better solution to use EventEmitter – Gabor Jan 09 '21 at 21:29
  • @Gabor I couldn't understand your actual problem or requrement. Whatever your child throws information i.e. in my example code, you'll get "you confirmed" on confirm button click and "you cancelled" on cancel button click. This you can handle afterClosed().sub ... by checking either it is this one or that one. Simple or better solution. – WasiF Jan 10 '21 at 07:06
  • I think it is a perfectly fine solution in general, but mostly what I do in Dlg is modifying an object and pass back the object to the parent. If you have 2 different functions like Confirm and Cancel with afterClose(), you have to maintain 2 different copy of the initial object (one is modified and one is not, or modifing them in place) But if I use EventEmitter I can manage to only send back data when Confirmed clicked. I think using EventEmitter result a more self-contained Dlg logic because in the parent I don't have to check if the object changed or not (I just accept if I get a one). – Gabor Jan 10 '21 at 08:43
  • 1
    It helped! keep the good work up. – Shoaib Khalil Jun 13 '22 at 15:56
19

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>
Akj
  • 7,038
  • 3
  • 28
  • 40
  • I used this but I don't know where and how to show the data in the console! I have two component in separate .ts file – fariba.j Aug 13 '18 at 05:11
  • Check demo. you need to subscribe data in parent component. – Akj Aug 13 '18 at 05:13
  • I think I forgot to use ngModel! I used it and the name input is working but the others are time pickers and return undefined or NAN! do you know what the problem is? – fariba.j Aug 13 '18 at 05:54
  • let me give you full example with different ts. – Akj Aug 13 '18 at 05:56
  • the previous answer was ok and my name input is good, but I have two input with date picker directive and just these two input return undefined – fariba.j Aug 13 '18 at 06:08
  • can you create stackblitz for demo ?? – Akj Aug 13 '18 at 06:12
  • i tried to but the time picker doesn't work there :(( – fariba.j Aug 13 '18 at 06:32
  • which time picker you are using?? – Akj Aug 13 '18 at 06:34
  • I'm using amazing-time-picker! if I remove ngmodel and name attribute they work good, I changed the start and end type to any in my interface and component but it still shows undefined – fariba.j Aug 13 '18 at 06:41
  • hey i got the solution – Akj Aug 13 '18 at 07:19
  • check demo plzz – Akj Aug 13 '18 at 07:22
  • @UnluckyAj I am looking through your example for a similar problem I have. I am using Angular Material's date picker with a formgroup/formcontrol. And you can't use ngModel with formcontrol. Do you know how to get the value with formcontrolname and pass the data to the parent component? – Flash Jan 22 '20 at 14:01
10

I use EventEmitter than subscribe to save event of the dialog. A bit more work but fits better to solve my problem. Because mostly you pass an object which you modify in the Dlg and maybe you doesn't want to pass back the modification. The solution requere some kind of Deep Copy solution.

In the dialog:

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)
 }

In the parent component:

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    
   })
 }
  
}
Gabor
  • 352
  • 5
  • 14
  • This solution is better when you don't want to close the dialog while passing data to the parent. – Csaba Dec 10 '21 at 10:42
2

In the dialog:

close(): void {
   this.dialogRef.close(data); //on close pass data to parent
  }

In the parent component:

    openDialog(row){
        const dialogRef = this.dialog.open(DialogComponent, {
          width: '880px',
          height: '480px',
          disableClose: true,
          data: data
        });

        dialogRef.afterClosed().subscribe(res => {
        console.log("value from dialog",res) //data from dialog
            this.headerDialogValue = res;
            });
      } 
anilam
  • 694
  • 6
  • 7
0

Below Solution works for me:

Matdialog Sharing Data Demo

enter image description here

Abdullah
  • 2,393
  • 1
  • 16
  • 29