so I have a scenario like this (simplyfied)
where:
main component
<my-list [month]="month"></my-list>
list component html
<li *ngFor="let item in list | async></li>
list component ts
list: Observable<ListItem[]>;
@Input() month: string;
...
ngOnInit() {
this.list = this._listService.list;
}
ngOnChanges(changes: SimpleChanges) {
if (changes.month && !!this.month) {
this._listService.getAllByMonth(this.month);
}
}
and finally list service
private _list: BehaviorSubject<ListItem[]>;
list: Observable<ListItem[]>;
private dataStore: {
list: ListItem[]
};
constructor(private _http: HttpClient) {
this.dataStore = { list: [] };
this._list= <BehaviorSubject<ListItem[]>>new BehaviorSubject([]);
this.list= this._list.asObservable();
}
public getAllByMonth(month: string) {
this._http.get<ListItem[]>(environment.apiPath + 'list/' + month)
.subscribe(
data => {
this.dataStore.list = data;
this._list.next(Object.assign({}, this.dataStore).list);
},
error => console.log('Could not load the list.')
);
}
For some reason getAllByMonth get's called many, many times... even though I change the month value only 1.
How should arrange things that getAllByMonth get's called ONCE when month value changes?
*ngFor
uses the equals ===
comparison to see if an Array
instance has changed. When the new array and old array are not the same reference, then all the elements in the collection are recreated.
To tell *ngFor
that it should not recreate an element, but re-use an element in the array. You have to use the trackBy
callback function.
<li *ngFor="let item in list | async; trackBy: trackItem"></li>
Then in your component
public trackItem(indx: number, item: LinkItem): number {
return item.id;
}
This function needs to return a value (number, string, object, etc) that yields true
for equal ===
comparisons. This is how ngFor
knows that it should re-use a component with that array item.
In my above example I assumed their is a unique number id
that can be used from each LinkItem
.
All of this stops the recursive calls to ngOnChanges
because those components were being recreated every time list
was updated with a new array instance.