0

I got a component hierarchy that looks a bit like this:

@Component({
...
})
export class A {

  constructor(private _serviceThatDeliversData: Service){} 

  getData(): Array<DataItem> {
    return this._serviceThatDeliversData.getData();
  }
}

html:

<b-selector [data]="getData()"></b-selector>

Child component:

@Component({
  selector: 'b-selector'
  ...
})
export class B {
  @Input() data: Array<DataItem>;
}

html:

<ul>
  <li *ngFor="let item of data">
    <c-selector [item]="item"></c-selector>
  </li>
</ul>

So I got a parent component 'A' that receives data from a service. The serviceThatDeliversData creates the DataItem list every time it receives data from a websocket. This list is then passed down to 'B' where every list entry is used as a base for sub components ('C', I omitted the C component here cause it basically just presents the input data 'item').

My problem now is the following: Since the list changes every time the service gets an update, the whole list of C components + the B component is newly created. I assume because the DataItem list changes completely Angular notices them as new entries (?) and the list as a new one itself (?) However in reality it might very well be that only one item was added or removed or just a field has changed in one of the items. So only one C component would needed to be removed/added/updated.

Since that is the case any animations happening in the view in any of the C components stop and start again once the list is recreated. That plus some other side effects I would like to avoid.

So my question is, is there a way to let Angular only update the C components which relate to a DataItem that actually changed internally AND the ones that were added or removed to/from the list, but leave the ones that have not changed untouched?

I have searched for a while and found that one could use changeDetection: ChangeDetectionStrategy.OnPush, however I have not found a working example (with a changing array and changing array elements (internally)) that would help me with my problem.

I assume one could store a copy of the list in 'B', check the changes manually and then react to them - which could also be done with an animation to signal the user which entry was removed or added - however that would require a way to stop Angular from updating (recreating) 'B' and the 'C' components at all. I take it using the same array (clearing it and repopulating it with the new entries) might work.

But I'd still like to know if this could be possible via raw Angular mechanisms.

1

1 Answer 1

1

The changedetection strategy OnPush only picks up changes to values decorated with the Input-decorator and only if the reference of the value changes.

For example myArray[1] = 'a'; only mutates the array and no new reference is created, therefore angular with OnPush strategy wouldn't pick up the change. You have to clone the array and make the change to create a new array reference.

Your problem with angular recreating the elements inside an ngForOf directive is descriped in another answer of me. Read the note at the end of the answer.

Sign up to request clarification or add additional context in comments.

2 Comments

But isn't it a problem that the array itself is created anew everytime the service gets updated? Wouldn't that already be regarded a change? And the list elements would all be recreated cause the list itself has changed? The linked answer looks promising regarding reuse of components in an ngFor loop. But I take it I would have to provide some sort of hash over all fields so that trackby would allow an update of a C component that actually needs changing. Also: what happens if a new component were to have the same 'identity' as one that was removed?
You could simply, as descriped in the answer, return the index inside the trackBy-function. The data would still get updated but angular won't recreate the DOM elements, which would prevent an animation on them. Therefore only if an item is added or removed from the list the animations for that element would be called.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.