Обновление части наблюдаемого, используемого в async pipe

У нас есть страница, на которой есть несколько разделов. В каждом разделе может быть карточка или стол. Наблюдаемый связывает данные в html с помощью async pipe. Теперь мне нужно обновить одну из таблиц, не обновляя все наблюдаемое.

  loadPageData(Id): void {
    this.sections$ = this.psService.getLoadPageData(Id).pipe(
      switchMap(sections => forkJoin(
        sections.map(section => this.psService.getSectionData(section.sectionID).pipe(
          map(card => Object.assign({}, section, { card })),
          switchMap(c => c.card.map(card => iif(() => card.cardTypeId === 3,
            from(this.psService.getTable(this.tablePerodID).pipe(map(table => Object.assign({}, c, { table })))),
            of(c))))
        ).concatAll()
        ))
      ));
  }

  updateTable(sectionId, tablePerodID) {
    let tableData;
    this.psService.getTable(tablePerodID).subscribe((data) => {
      tableData = data;
    });
    this.sections$.forEach(sections => {
      sections.forEach(section => {
      if (section.sectionID === sectionId) {
        section.table = sectionData;
      }
    });
    });
  }

а HTML выглядит так:

<div *ngFor="let section of sections$ | async;">
  <div *ngFor="let card of section.card | sort:'displayOrder';">
    <app-card></app-card>
    <div *ngIf="card.cardTypeName === 'Table'">
      <app-table></app-table>
    </div>
   </div>
 </div>

Теперь проблема в том, что когда я пытаюсь загрузить данные для таблицы с помощью updateTable [когда пользователь изменяет tablePerodID], он ничего не делает. Я пробовал .next(), но он обновляет полностью section$. В любом случае я могу только обновить таблицу?


person user1375481    schedule 06.03.2020    source источник
comment
Правильно ли я думаю, что таблицы одинаковы в каждом разделе, поскольку они происходят из одного и того же уровня компонентов tablePeriodID? Значит, всякий раз, когда tablePeriodID обновляется, все таблицы обновляются одинаково?   -  person Kurt Hamilton    schedule 13.03.2020
comment
Таблицы могут быть разными для каждого раздела. В каждом заголовке таблицы разделов есть раскрывающийся список с точками. Идея состоит в том, чтобы обновить только таблицу в том конкретном разделе, где изменяется это раскрывающееся значение.   -  person user1375481    schedule 13.03.2020
comment
Можете ли вы создать это как минимальный стек? На данный момент это можно интерпретировать по-разному.   -  person Kurt Hamilton    schedule 13.03.2020
comment
Кроме того, сколько существует возможных таблиц? Должны ли они всегда загружаться асинхронно или все они могут быть изначально загружены в память?   -  person Kurt Hamilton    schedule 14.03.2020
comment
По крайней мере, в методе updateTable блок forEach this.sections$.forEach должен находиться в блоке subscribe this.psService.getTable, потому что этот вызов является асинхронным. Хотя это может не решить вашу непосредственную проблему, оно решает проблему, с которой вы, скорее всего, столкнетесь по ходу дела. Я мог бы опубликовать ответ, чтобы проиллюстрировать это, если мой комментарий недостаточно ясен   -  person mfalade    schedule 14.03.2020
comment
Почему вы не обрабатываете каждую секцию самостоятельно? Вы получаете раздел, создаете ng-контейнер с ngFor и создаете app-section. В этом разделе вы загружаете все данные для этого раздела. В этом разделе вы делаете то же самое с карточкой и столом. Итак, приложение-таблица и приложение-карта. В каждом из этих компонентов вы загружаете данные в ngOnInit. Если таблица будет изменена с помощью раскрывающегося списка с периодами, вы легко активируете метод снова в этой таблице. Это в основном суть angular. Разделяй и властвуй! если у вас есть значение, которое передается от родителя к потомку, вы используете ViewChild, если не нормально. #databinding   -  person liqSTAR    schedule 19.03.2020


Ответы (2)


Это похоже на неправильное понимание того, как работают трубы.

Сравнение

Подумайте о водопроводных трубах.

Представьте, что вам нужно из крана налить сахарную воду.

Если вы возьмете стакан воды из-под крана и добавите в него сахар, вы больше не сможете снова налить эту воду в кран.

Решением было бы добавить кусок трубы с системой для добавления сахара перед тем, как взять стакан воды.

Каналы rxjs такие же, как только вы подпишетесь, пути назад нет. (если вы не создадите новую трубу и не нальете в нее воду;)

Решение состоит в том, чтобы прокачать все по трубопроводу. Только subscribe, когда вы хотите получить значение. Это также улучшает производительность.

Решение

this._cache$: BehaviorSubject<Section[]> = new BehaviorSubject<Section[]> ([]);
this.sections$: Observable<Section[]>;
loadPageData(id: number): void {
    // subcribe once and save the values in the cache
    this.fetchPageData(Id).subscribe(data => this._cache$.next(data));

    // section$ will emit values when caché changes
    this.sections$ = this._cache$.asObservable();
}

fetchPageData(Id): Observable<Section[]> {
    // call your data service
    return this.psService.getLoadPageData(Id).pipe(
       //...
    );
}

updateTable(sectionId, tablePerodID) {
    let tableData;
    this.psService.getTable(tablePerodID).subscribe((data) => {
      tableData = data;
    });

    this.sections$ = this.sections$.pipe(
        map(sections => sections.map(section =>
        {
            if (section.sectionID === sectionId) {
                section.table = tableData;
             }
        })
    );
}

Вместо того, чтобы подписываться на наблюдаемое, выполните операцию обновления по конвейеру.

updateTable обновит sections$ наблюдаемый, а асинхронный конвейер html обновит результаты.

Использование _cache$ дает еще одно улучшение производительности, потому что каждый раз, когда вы обновляете канал, он не будет вызывать вашу службу данных. (Не обязательно использовать)

Уточнение: это обновит ваш канал в вашем приложении, а не данные в базе данных или где-то еще.

person adrisons    schedule 17.03.2020
comment
Это решение не помогло мне, поскольку я пытаюсь избежать обновления всего раздела $ observable. - person user1375481; 24.03.2020
comment
Если вы используете caché, данные будут извлечены только при вызове loadPageData, тогда вам следует использовать наблюдаемый. Подписка на sections$ observable не вызовет http-вызовы - person adrisons; 24.03.2020

Спасибо @adrisons за то, что поделились идеей кеширования. Вот решение, которое сработало для меня. Он немного отличается от ответа @adrisons тем, что не использует наблюдаемое.

  _cached: any; 
  sections$: Observable<any>;

  loadPageData(Id): void {
    // Load data from server
    this.sections$ = this.psService.getLoadPageData(Id).pipe(
      switchMap(sections => forkJoin(
        sections.map(section => this.psService.getSectionData(section.sectionID).pipe(
          map(card => Object.assign({}, section, { card })),
          switchMap(c => c.card.map(card => iif(() => card.cardTypeId === 3,
            from(this.psService.getTable(this.tablePerodID)
            .pipe(map(table => Object.assign({}, c, { table })))),
            of(c))))
        ).concatAll()
        ))
      ), first(), // Tap and get the first emit and assign to cache
      tap((data: any) => {
        this._cached = data // Caching the data without subscribing.
      }));
  }

  updateTable(sectionId, tablePerodID) {
    // Get table Data from backend and update cache
    this.psService.getTable(tablePerodID).subscribe((data) => {
      this._cached.map(section => {
        if (section.sectionID === sectionId) {
          section.table = data;
        }
      });
    });
    // Refresh async observable from cache
    this.refreshFromCache();
  }

  refreshFromCache() {
    this.sections$ = of(this._cached)
  }
person user1375481    schedule 01.04.2020