Tawm
Tawm

Reputation: 545

*ngFor: Parent element at array index resets Child component

I have a Parent Component with an array of Objects.

I use an *ngFor loop to populate Child Components via an @Input() with the element at each index.

If I change the object at an index, the child component completely resets instead of just accepting the new Input and maintaining it's other properties.

Stackblitz minimal example

TypeScript:

export interface MyObject {
  a: string;
  b: string;
}

export class Parent {
  objectArray: MyObject[] = [
    {a: 'string A', b: 'string B'}
  ];

  changeAnObject() {
    const sameObject: MyObject = {a: 'string A', b: 'string B'};
    this.objectArray[0] = sameObject;
  }
}

export class Child {
  @Input() inputObject: MyObject;
  selected = false; // Some other property to maintain
}

Parent HTML:

// 3 different ways to populate inputObject

<div *ngFor="let object of objectArray">
  <app-child [inputObject]="object"></app-child> // does not maintain "selected" property
</div>

<div *ngFor="let object of objectArray; let index = index">
  <app-child [inputObject]="objectArray[index]"></app-child> // does not maintain "selected" property
</div>

<div>
  <app-child [inputObject]="objectArray[0]"></app-child> // DOES maintain "selected" property
</div>

<button (click)="changeAnObject()">Change Object</button>

Child HTML:

<div (click)="selected = !selected">
    a: {{inputObject.a}}
    b: {{inputObject.b}}
    SELECTED: {{selected}}
</div>

Result

In the parent HTML, [inputObject]="objectArray[0]" is the only solution I've found which maintains the other properties of Child when changing the element in objectArray[0].

This is not good enough for me as I have many objects to display.

Is there a better way to send data into Components without resetting them entirely? I have attempted using Angular Accessors with @Input() set inputObject {...} but it has not worked to maintain the Component's properties. That is, the constructor executes again when inputObject changes, resetting all properties to default values.

Upvotes: 1

Views: 1409

Answers (1)

Elias Dal Ben
Elias Dal Ben

Reputation: 439

You need to track by your object by some index, that will not change after you execute your changeAnObject action.

<div *ngFor="let object of objectArray; let index = index; trackBy: trackByFn">

with your trackByFn being:

trackByFn(index, item) {
   return item.a; 
}

Just do it like this and it will work! :)

What it does is that by tracking the object by an unique and non changing id, it will not reiterate through the ngFor loop because it detected that the id that is tracking the loop has not been changed

My example was based on your stackBlitz

Upvotes: 2

Related Questions