Search code examples
angularangular-material

Passing Data from component to Dialog but does not 2 way model binding


Currently, I have fruits component and update fruits component. Fruit component is responsible for showing different chips of fruit and a button to update those chips. Currently selected fruits are passed in the dialog data

Fruits Component Html

<div>
  <mat-chip-list *ngFor="let fruit of selectedFruits">
    <mat-chip>{{ fruit  }}</mat-chip>
  </mat-chip-list>
</div>
<button (click)="fruitsUpdateDialog()">
  Update Fruits
</button>

  fruitsUpdateDialog() {
    this.dialog.open(FruitsUpdateComponent, {
        data: { selectedFruits: this.selectedFruits }
    });
  }

FruitsUpdateComponent - This gets the fruits correctly which is what I want but when I remove a fruit from the mat chips, Fruits Component Html automatically gets updated which I do not want. I only want to pass data from Fruits html to fruits update not the other way. How can I resolve that?

export class FruitsUpdateComponent implements OnInit {
  visible = true;
  selectable = true;
  removable = true;
  separatorKeysCodes: number[] = [ENTER, COMMA];
  fruitFormControl = new FormControl();
  fruits: any;
  allFruits: any;

  @ViewChild('fruitInput', { static: false }) fruitInput: ElementRef<
    HTMLInputElement
  >;
  @ViewChild('auto', { static: false }) matAutocomplete: MatAutocomplete;

  constructor(
    @Inject(MAT_DIALOG_DATA) public data) {
      this.allFruits = //All Fruits JSON;
    };
  }

  ngOnInit() {
    this.fruits= this.data.selectedFruits;
  }

  remove(fruit: string, index): void {
    this.fruits.splice(index, 1);
  }

  selected(event: MatAutocompleteSelectedEvent): void {
    this.fruits.push(event.option.value);
    this.fruitInput.nativeElement.value = '';
    this.fruitFormControl.setValue(null);
  }

 

HTML

  <mat-dialog-content>
    <mat-form-field>
      <mat-chip-list #chipList>   
          <mat-chip
            *ngFor="let fruit of fruits ; let fruitIndex= index"
            [selectable]="selectable"
            [removable]="removable"
            (removed)="remove(fruit , fruitIndex)"
          >
            {{ fruit }}          
          </mat-chip>     
          <input
            placeholder="What"
            #fruitInput
            [formControl]="fruitFormControl"
            [matAutocomplete]="auto"
            [matChipInputFor]="chipList"
            [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
          />     
      </mat-chip-list>
      <mat-autocomplete
        #auto="matAutocomplete"
        (optionSelected)="selected($event)"
      >
        <mat-option *ngFor="let fruit of allFruits" [value]="fruit">
          {{ fruit }}
        </mat-option>
      </mat-autocomplete>
    </mat-form-field>
  </mat-dialog-content>

So fruits are correctly passed to the dialog box which is what I want but when I remove a fruit from the mat chips, Fruits Component Html automatically gets updated which I do not want. I only want to pass data from Fruits html to fruits update not the other way. I only want one way binding. How can I resolve that?


Solution

  • the reason is, you are passing an object to the dialog component, and as javascript object works by reference so if your object has changed in the dialog component, it will also change in your main component. so the best way to pass data to dialog is by making variables immutable.

    in your fruits.component.ts

    fruitsUpdateDialog() {
        this.dialog.open(FruitsUpdateComponent, {
            // map function will return copy of original fruits.but it 
            // will not point to same reference 
            const tempFruits = this.selectedFruits.map(fruit => fruit);
            data: { selectedFruits: tempFruits }
        });
      }