angular: метод связывания + параметр с асинхронным каналом в шаблоне - PullRequest
1 голос
/ 13 апреля 2019

Я играю с firebase и шаблоном коллекции «многие ко многим».см .: https://angularfirebase.com/lessons/firestore-nosql-data-modeling-by-example/#Many-to-Many-Middle-Man-Collection

Но я борюсь за то, чтобы шаблон "завершил соединение" после того, как я получил массив идентификаторов в коллекции.

Я хочу отобразить 'join'элементы в шаблоне с async pipe, такие как:

<div *ngFor="let id of itemIds">
  <div *ngIf="(getById$(id) | async) as item">{{item.name}}</div>
</div>

, но Angular change detection вызывает getById$() несколько раз, я каждый раз получаю новый Observable, и ... браузер зависает.

Я взломал решение, кэшировав Observable, но это кажется неправильным.

  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 Ответ

0 голосов
/ 13 апреля 2019

Давайте сыграем логически. Какие бы элементы ни использовались в коллекции *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>

Таким образом, наблюдаемые останутся неизменными для своих соответствующих идентификаторов, и все это будет нормально работать с обнаружением изменений.

Итак, подведем итог: вам нужно хранить коллекцию наблюдаемых и обновлять ее только тогда, когда изменяется исходная коллекция идентификаторов, и вам необходимо сохранять наблюдаемые экземпляры для идентификаторов, для которых они были созданы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...