1

I am generating dynamic mat-inputs using *ngFor. And I want to store each [(ngModel)] value in a different array (not the *ngFor one) using index of *ngFor elements. Here's what I'm doing:

<div  *ngFor="let item of items;let id = index;">

<mat-form-field >
  <mat-select  [(ngModel)]="differentArray[id].first"  (ngModelChange)="onSelection()">
    <mat-option *ngFor="let number of arrayOfNumbers" [value]="number">{{number}}</mat-option>
  </mat-select>
</mat-form-field> 

<mat-form-field>
  <mat-select  [(ngModel)]="differentArray[id].second"  (ngModelChange)="onSelection()">
    <mat-option *ngFor="let number of arrayOfNumbers" [value]="number" >{{number}}</mat-option>
  </mat-select>
</mat-form-field>

<mat-form-field >
  <mat-select  [(ngModel)]="differentArray[id].third"  (ngModelChange)="onSelection()">
    <mat-option *ngFor="let number of arrayOfNumbers" [value]="number">{{number}}</mat-option>
  </mat-select>
</mat-form-field>

</div>

But the values for all different *ngFor are getting same. Why does this happen? And how can I properly do this with each element id?

nyxtron
  • 41
  • 8
  • 1
    You are using the same onSelection() method for all select inputs. Maybe there's something in this method? – N1gthm4r3 Jun 18 '20 at 13:27
  • No. Nothing happens in them. Just console.log till now. – nyxtron Jun 18 '20 at 13:39
  • can you share an example of what you mean by `But the values for all different *ngFor are getting same`? It looks like you are *ngFor ing over the same array 3 times. – joshvito Jun 18 '20 at 13:44
  • The way you use `differentArray` is peculiar. Could you share how you define it? – ruth Jun 18 '20 at 13:47
  • I have an array of ```items```, and for each ```item```, I have to take these three drop down values. Then, I have to save those three values in a different array according to the index of the ```item``` in ```items```. So basically, I have to save as ```differentArray=[{first:"selected value from dropdown",second:"selected value from ddropdown",third:"selected value from dropdown"},{},{},{}]``` for all the item in ```ngfor``` . But each object of differentArray is getting the same value. – nyxtron Jun 18 '20 at 13:55

1 Answers1

0

From the way you've defined the property bindings to the [(ngModel)] directives, the differentArray should be of the form

differentArray = [
  { first: 0, second: 0, third: 0 },
  { first: 0, second: 0, third: 0 },
  { first: 0, second: 0, third: 0 }
]

Working example: Stackblitz

Update

Instead of pushing it in the parent, you could bind the @Input() decorator to a setter and initialize the differentArray directly in the child. But regardless of whether you're doing in the parent or child, the most important thing to remember is the object will be pushed by reference. So any changes to one of the object will also affect other objects. So you need to use JSON.parse(JSON.stringify(obj)) to create deep clones of the obj. Try the following

parent.component.ts

export class ParentComponent {
  items = ['item1', 'item2', 'item3'];
  arrayOfNumbers = [1, 2, 3, 4, 5];
  differentArray = { first: 0, second: 0, third: 0 };

  constructor() {}
}

parent.component.html

<app-child [items]="items" [arrayOfNumbers]="arrayOfNumbers" [differentArray]="differentArray"></app-child>

child.component.ts

export class ChildComponent {
  _differentArray: Array<any> = [];

  @Input() items: Array<any> = [];
  @Input() arrayOfNumbers: Array<any> = [];
  
  @Input() set differentArray(obj: any) {
    this.items.forEach(item => 
      // notice the deep clone using `JSON.parse(JSON.stringify(obj)` here
      this._differentArray.push(JSON.parse(JSON.stringify(obj)))
    );
  }

  constructor() {}

  onSelection() {
    console.log('selected: ', this._differentArray);
  }
}

child.component.html

<div *ngFor="let item of items; let id=index;">
  <mat-form-field >
    <mat-select  [(ngModel)]="_differentArray[id].first"  (ngModelChange)="onSelection(id)">
      <mat-option *ngFor="let number of arrayOfNumbers" [value]="number">{{number}}</mat-option>
    </mat-select>
  </mat-form-field> 

  <mat-form-field>
    <mat-select  [(ngModel)]="_differentArray[id].second"  (ngModelChange)="onSelection(id)">
      <mat-option *ngFor="let number of arrayOfNumbers" [value]="number" >{{number}}</mat-option>
    </mat-select>
  </mat-form-field>

  <mat-form-field >
    <mat-select  [(ngModel)]="_differentArray[id].third"  (ngModelChange)="onSelection(id)">
      <mat-option *ngFor="let number of arrayOfNumbers" [value]="number">{{number}}</mat-option>
    </mat-select>
  </mat-form-field>
</div>

Working example: Stackblitz

Community
  • 1
  • 1
ruth
  • 29,535
  • 4
  • 30
  • 57
  • Apparently the diffferentArray which I'm using, is dynamically generated from parent and sent via ```@Input()``` . And this method is not working for that. I have defined the array same way as you. I tried the same defining static array like you, it works. – nyxtron Jun 18 '20 at 14:14
  • Could you show how the incoming array from the parent looks? Then we could try to modify the `ngModel` binding in the child. – ruth Jun 18 '20 at 14:15
  • ```differentArray =[{first: 1, second: 0, third: 0}]``` and as ```items``` array (the one used in ```*ngFor``` ) in parent increase, I keep pushing ```{first: 1, second: 0, third: 0}``` onto ```differentArray``` and pass it to the child. – nyxtron Jun 18 '20 at 14:29
  • I may have found the answer. The objects were assigned by reference so any changes to one object was also modifying the other objects. You could create a deep close using `JSON.parse(JSON.stringify(obj))`. I've updated the answer. – ruth Jun 18 '20 at 14:55
  • I tried and this unfortunately does not work because in your case the arrays are already defined, but in my case they are dynamic. So I think the ```@Input()``` occurs when i```tems``` array is empty. Thus, before anything occurs, it tries to go inside forEach and thus doesn't work. Now, when first ```item``` is pushed on ```items```, it becomes defined, but nothing happens as it's only a reference. This, was the only reason why I tried getting my ```differentArray``` from the parent. – nyxtron Jun 18 '20 at 15:34
  • @nyxtron: It's difficult to understand how you've set it up from description. Could you please post your setup including parent child controller and templates in your question? – ruth Jun 18 '20 at 15:59
  • It's same as your code, except the ```items``` array is generated via an api in the parent component and I don't have the length. That's why all the problems. Unfortunately, I can't post the entire code. Sorry. – nyxtron Jun 18 '20 at 16:11
  • You don't have to post the actual `items` array or the API URL. Just post how you construct the `differentArray` array from the `items` array. Or do you do it *exactly* as shown in the answer? – ruth Jun 18 '20 at 16:15
  • I do it just like your answer. Only difference is the ```items``` array is generated by clicking of the user so I just don't know the length of the ```items``` array. That's the problem. – nyxtron Jun 18 '20 at 16:29
  • Hey thanks for your help! Just an update if it matters, since now I realized that the problem was the array was being passed through ```@Input```, I only got the reference and not the entire array, thus all the values were being changed. I used this https://stackoverflow.com/a/42962723/13687508 to take ```@Input()``` and kept the rest same as your code, everything works fine! – nyxtron Jun 19 '20 at 02:51
  • @nyxtron: From my answer: *regardless of whether you're doing in the parent or child, the most important thing to remember is the object will be pushed by reference, so you need to use `JSON.parse(JSON.stringify(obj))` to create deep clones of the object* – ruth Jun 19 '20 at 05:27