0

I'm trying to pass two strings in Angular Material. The HTML code in the Mat Dialog content is to create 2 cdk drop lists where the values can be exchanged between arrays.

I went through this answer for a similar problem and tried implenting it but it didn't work. How to pass data to dialog of angular material 2

This is my code for the dialog class as well as the class where the dialog class is called.

export class TableComponent implements OnInit {
   displayedColumns: string[] = ['A', 'B', 'C'];
   availableColumns: string[] = [];

   openDialog() {
     const dialogRef = this.dialog.open(ColumnsDialog, {
     width: '500px',
     height: '500px',
     data: {
       displayedColumns: this.displayedColumns,
       availableColumns: this.availableColumns
     },
     });
   }  

  @Component({
  selector: 'columns-dialog',
  templateUrl: 'columns-dialog.html',
  changeDetection:ChangeDetectionStrategy.OnPush
 })

 export class ColumnsDialog {
   constructor(public dialogRef: MatDialogRef<ColumnsDialog>,
    @Inject(MAT_DIALOG_DATA) public data: any) {}

   onCloseClick(): void {
    this.dialogRef.close(true);
  }


 drop(event: CdkDragDrop<string[]>) {
   if (event.previousContainer === event.container) {
    moveItemInArray(event.container.data, event.previousIndex, 
    event.currentIndex);
   } else {
     transferArrayItem(event.previousContainer.data,
                    event.container.data,
                    event.previousIndex,
                    event.currentIndex);
   }
 } 
}

This is my code for the contents of the mat-dialog.

<mat-dialog-content>
<div >
    <h2>Unselected Columns</h2>

                    <div
                      cdkDropList
                      #availableColumnsList="cdkDropList"
                      [cdkDropListData]="availableColumns"
                      [cdkDropListConnectedTo]="[displayedColumnsList]"

                      (cdkDropListDropped)="drop($event)">
                      <div  *ngFor="let item of availableColumns" cdkDrag>{{item}}</div>
                    </div>
                  </div>

                  <div >
                    <h2>Selected Columns</h2>

                    <div
                      cdkDropList
                      #displayedColumnsList="cdkDropList"
                      [cdkDropListData]="displayedColumns"
                      [cdkDropListConnectedTo]="[availableColumnsList]"

                      (cdkDropListDropped)="drop($event)">
                      <div *ngFor="let item of displayedColumns" cdkDrag>{{item}}</div>
                    </div>
                  </div>
 </mat-dialog-content>

When I pass the displayedColumns and availableColumns string, I want 2 cdk drop lists where the values of the array displayeColumns can be passed to available columns and vice versa. I was able to achieve this when I was using a dropdown menu for the button upon clicking which this dialog is opening now so I know the code for the cdk drop lists works. I think the issue is in passing data to the mat-dialog.

Also, I don't want the mat-dialog to create its own copy of displayedColumns and availableColumns for the display since I'm using these string arrays to change the displayed columns of a Angular Material Table.

Akshat Sood
  • 345
  • 2
  • 7
  • 19

1 Answers1

2

The dialog data provided to the dialog through the MatDialogConfig object when the dialog is opened is usually a copy of the 'original' data. So changes made from the dialog are limited to the dialog alone. The standard way to use MatDialog to modify the 'original' data is through the MatDialogRef functions close() and afterClosed().

close() should be called with the dialog data that has been changed. You can pass anything you want, but typically you pass the data object that you injected into the dialog class or some part of it:

this.dialogRef.close(this.data);

afterClosed() should be subscribed to so that a handler is called when the dialog is closed. The handler receives the data passed to close() and then typically uses it to update the 'original' data:

dialogRef.afterClosed().subscribe(result => {
  // do something with the result
});

The handler will only get triggered when close() is called. Usually your dialog will have a close button for this. Clicking outside the dialog or pressing escape is not a 'close' action (it is a 'cancel' action) so the handler won't (and shouldn't) be called.

Your code doesn't do any of this. You have a function that closes the dialog, but it passes a value of 'true', not the dialog data, and it is never called anywhere anyway, as you have not implemented a handler for dialog closure.

To fix that do the following:

TableComponent

displayedColumns: string[] = ['A', 'B', 'C'];
availableColumns: string[] = [];

openDialog() {
  const dialogRef = this.dialog.open(ColumnsDialog, {
    width: '500px',
    height: '500px',
    data: {
      displayedColumns: this.displayedColumns,
      availableColumns: this.availableColumns
    },
  });

  dialogRef.afterClosed().subscribe(result => {
    this.displayedColumns = result.displayedColumns,
    this.availableColumns = result.availableColumns
  });
}

ColumnsDialog

constructor(public dialogRef: MatDialogRef<ColumnsDialog>,
  @Inject(MAT_DIALOG_DATA) public data: any) {}

onCloseClick(): void {
  this.dialogRef.close(this.data);
}

You also of course have to use the onCloseClick() function from a button on your dialog:

<div mat-dialog-actions>
    <button mat-button (click)="onCloseClick()">Update</button>
</div>

You can do that entirely with HTML if you want, so you don't need to define the function:

<div mat-dialog-actions>
    <button mat-button [mat-dialog-close]="data">Update</button>
</div>
G. Tranter
  • 16,766
  • 1
  • 48
  • 68