0

As the title says I need to update one value in array.

<div *ngFor="let item of data; let i = index" >
  <div class="full-w" fxLayout='row' fxLayoutAlign='start center' fxLayoutGap='12px'>
    <span class="title">Color: </span>
    <span>{{item.color}}</span>
  </div>
  <mat-divider class="full-w"> </mat-divider>

  <div *ngFor="let e of item.sizeQuantity; let j = index" fxLayout='row' fxLayoutAlign='space-between center'
    fxLayoutGap='12px'>
    <span class="title">Size: </span>
    <span class="size">{{e.size}}</span>
    <mat-form-field>
      <input matInput type="number" placeholder="Quantity" [value]="e.quantity" #quantity />
    </mat-form-field>

    <button mat-button (click)="updateQuantity(quantity.value, item, i, j)">Add</button>
  </div>
</div>

and this is the initial data

enter image description here

and when I enter the data in one field, e.g. under black>xs, it changes on beige>xs too (if there is blue>xs or more it gets updated)

enter image description here

enter image description here

I tried several approaches but it keeps updating values at every position

  updateQuantity(value, item, ind, j) {

    this.data.forEach(e => {
      if (e.color == item.color) {
        e.sizeQuantity[j].quantity = parseInt(value);
      }
    })
  }

Or like this

  updateQuantity(value, item, ind, j) {
    this.data.find(e => e.color == item.color).sizeQuantity[j].quantity = parseInt(value); 

  }

Am I missing something?

//EDIT:

Data is created like this:

       let quantity = [];
        let sizeQuantity = [];

        this.selectedSizes.forEach(e => {
          sizeQuantity.push({ size: e, quantity: 0 })
        })

        this.selectedColors.forEach(e => {
          quantity.push({ color: e, sizeQuantity })

        })

        this.dialog.open(AddQuantityComponent, {
          data: quantity
        })

where sizes and colors arrays are dynamically created based on user selection:

this.selectedColors  = ['black', 'beige', 'blue'];
this.selectedSizes = ['XS', 'S'];
Srdan
  • 229
  • 6
  • 12
  • How does the `this.data` get its initial value in `add-quanitity.component.ts`? I suspect that the initial values of the colors are in one array instance and the colors contain reference to that instance. And as the quantity at one color is updated, the original array is updated and because of this all the colors get the same quantity value. – Milan Tenk Apr 12 '20 at 08:00
  • this.data is injected as @Inject(MAT_DIALOG_DATA) private data; – Srdan Apr 12 '20 at 08:22
  • Thank you for the info. How does the origin of the injected data look like? I'm interested in how the default displayed data is created before it is injected in the component. – Milan Tenk Apr 12 '20 at 08:38
  • ok check the edit and thanks – Srdan Apr 12 '20 at 09:01
  • 1
    Thank you, see my answer below. In the above code snippet `this.selectedSizes` and `this.selectedColors` are inversely. I assumed in my answer that `this.selectedSizes =['XS', 'S'];` and `this.selectedColors = ['black', 'beige', 'blue'];`. – Milan Tenk Apr 12 '20 at 09:31
  • yea, you're right, edited – Srdan Apr 12 '20 at 10:23

1 Answers1

1

The quantity values are updated in each color, because they share the same sizeQuantity instance. The problem roots at this method

this.selectedColors.forEach(e => {
  quantity.push({ color: e, sizeQuantity })
})

Here a deep copy has to be created from the sizeQuantity, this means that following could be used:

this.selectedColors.forEach(e => {
  let sq = JSON.parse(JSON.stringify(sizeQuantity));
  quantity.push({ color: e, sizeQuantity: sq });
});

It is tricky that JSON serialize/deserialize is used for creating deep copy, but this is the easiest and most backward compatible method. For further details about creating a deep copy check out this stackoverflow conversation.

Milan Tenk
  • 2,415
  • 1
  • 17
  • 24
  • 1
    Since contents of the `sizeQuantity` are primitive types (string). You don't need a deep clone here. Using the array spread operator or slice method is enough. – Eldar Apr 12 '20 at 09:57