Давайте сыграем логически. Какие бы элементы ни использовались в коллекции *ngFor
, они должны оставаться неизменными , чтобы предотвратить обнаружение изменений при повторной визуализации представлений элементов и, следовательно, повторной подписки на наблюдаемые объекты. Следовательно, вы должны где-то хранить коллекцию наблюдаемых и не создавать их на лету. То, как вы это сделали, в значительной степени то, что вам действительно нужно. Мы можем улучшить его, хотя и ненамного.
Когда вы получаете itemIds
коллекцию, все, что вам нужно, это выполнить одноразовое сопоставление их с какой-либо коллекцией, содержащей наблюдаемые, и поместить эту коллекцию в шаблон вместо просто идентификаторов источника. Примерно так:
private _items: any[] = []; // put your type here instead of <any>
get items$(): any[] {
return this._items;
}
set itemIds(value: any[]) {
if (!value) {
this._items = [];
return;
}
// I know that it's pretty ugly and inefficient implementation,
// but it'll do for explaining the idea.
this._items = value.map(id => {
// this is the essential part: if observable had already been created for the id
// then you _must_ preserve the same object instance in order to make change detection happy
// you can also try to use trackBy function in the *ngFor definition
// but I think that it will not help you in this case
let item$ = this._items.find(i => i.id === id);
if (!item$) {
item$ = {
id: id,
obs$: this.getById(id)
};
}
return item$;
});
}
А потом в шаблоне:
<div *ngFor="let item$ of items$">
<div *ngIf="(item$.obs$ | async) as item">{{item.name}}</div>
</div>
Таким образом, наблюдаемые останутся неизменными для своих соответствующих идентификаторов, и все это будет нормально работать с обнаружением изменений.
Итак, подведем итог: вам нужно хранить коллекцию наблюдаемых и обновлять ее только тогда, когда изменяется исходная коллекция идентификаторов, и вам необходимо сохранять наблюдаемые экземпляры для идентификаторов, для которых они были созданы.