#angular #firebase #angularfire2
# #angular #firebase #angularfire2
Вопрос:
Я играю с firebase
шаблоном сбора «многие ко многим». смотрите: https://angularfirebase.com/lessons/firestore-nosql-data-modeling-by-example/#Many-to-Many-Middle-Man-Collection
Но я изо всех сил пытаюсь найти шаблон для «завершения соединения» после того, как получу массив идентификаторов в коллекции.
Я хочу отобразить «объединенные» элементы в шаблоне async pipe
следующим образом:
<div *ngFor="let id of itemIds">
<div *ngIf="(getById$(id) | async) as item">{{item.name}}</div>
</div>
но Angular change detection
вызывает getById$()
несколько раз, я каждый раз получаю новую наблюдаемую, и …браузер зависает.
Я взломал решение, кэшируя наблюдаемое, но это кажется неправильным.
getById$(uuid:string):Observable<T>{
this['get$_'] = this['get$_'] || {}; // cache in component
const cached$ = this['get$_'][uuid];
if (cached$) return cached$ as Observable<T>;
return this['get$_'][uuid] = this.get(uuid) as Observable<T>;
}
Есть ли лучший шаблон?
Ответ №1:
Давайте разыграем это логически. Какие бы элементы ни использовались в *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>
Таким образом, наблюдаемые останутся неизменными для их соответствующих идентификаторов, и все это будет нормально работать при обнаружении изменений.
Итак, подводя итог: вам нужно сохранить коллекцию наблюдаемых и обновлять ее только при изменении исходной коллекции идентификаторов, и вам нужно сохранить наблюдаемые экземпляры для идентификаторов, для которых они были созданы.