Привязка массива к компоненту в шаблоне работает только при загрузке страницы - PullRequest
2 голосов
/ 29 апреля 2020

У меня есть компонент (для панели навигации), который находится в моем шаблоне макета, и я использую сервис инъекций, чтобы компоненты могли изменять содержимое панели навигации в зависимости от их собственных логи * c.

Вот часть разметки шаблона

default-layout.component. html

<app-sidebar-nav [navItems]="defaultLayoutService.items$ | async" [perfectScrollbar] [disabled]="appSidebar.minimized"></app-sidebar-nav>

default-layout.component.ts

constructor(public defaultLayoutService: DefaultLayoutService) {
}

default-layout-service.ts

import { navItems, ICustomNavData } from '../../_nav';

items$: Observable<ICustomNavData[]>;
navItems = <ICustomNavData[]>navItems; //cast to a custom interface I created.

setNavItemAttribute(itemKey: string, itemAttribute: string, text: string) {
    let menuItem = this.navItems.find(r => r.key == itemKey);

    if (menuItem && menuItem.hasOwnProperty(itemAttribute)) {
        menuItem[itemAttribute] = text;
    }

    console.log(this.navItems); //this outputs the items and shows they were modified correctly
    this.items$ = new Observable(ob => { ob.next(this.navItems); })

}

Итак, у меня есть компоненты, которые также имеют доступ к DefaultLayoutService и вызывают setNavItemAttribute с разные параметры.

Работает, но только когда я обновляю sh страницу. При просмотре в режиме одной страницы приложения, при вызове setNavItemAttribute с другими параметрами, элементы навигации остаются прежними.

Правильно ли я здесь работаю с наблюдаемым?

1 Ответ

2 голосов
/ 29 апреля 2020

Правильно ли я здесь работаю с Наблюдаемым?

Я вижу здесь 2 потенциальных проблемы:

  1. Вы устанавливаете items$ Наблюдаемое в вашем обслуживать новый экземпляр каждый раз, когда вы обновляете соответствующий элемент в текущем массиве.
  2. Вы устанавливаете значение этого Observable для того же экземпляра массива, поэтому Angular обнаружение изменений не делает ' Не думаю, что массив изменился (хотя значения в пределах он имеет).

Давайте обратимся к этим:

Вы можете использовать посредника Subject для запуска значений, передаваемых на Observable из вашего сервиса, и вызова .next для него, когда вы хотите обновить значения.

Вы делаете это, выставляя Observable как обычно, построен из Subject. Это сделано для того, чтобы внешние факторы не могли непреднамеренно / злонамеренно вызвать .next непосредственно по теме.

могло бы выглядеть примерно так:

export class DefaultLayoutService {
  private _navItems: <ICustomNavData[]>;
  private readonly _itemsSubject = new Subject<ICustomNavData[]>();

  items$: Observable<ICustomNavData[]> = this.itemsSubject.asObservable();

  setNavItemAttribute(itemKey: string, itemAttribute: string, text: string) {
    // code to update corresponding item in navItems array

    this._itemsSubject.next(this.navItems);
  }
}

, поскольку Observable получен из Subject, он будет выдавать последнее значение, которое вы передали Субъекту в вашем методе обслуживания.

Помните, что вам нужно будет вызвать .next с первым значением _itemsSubject, когда вы Сначала инициализируйте компонент и массив, чтобы его можно было выдать на Observable.


При просмотре в режиме одной страницы приложения, вызывая вызов setNavItemAttribute с различными параметрами, навигация элементы остаются прежними.

Поскольку ссылка на сам массив не изменилась, обнаружение изменений Angular не выполняет повторную визуализацию с новыми значениями внутри массив. Кажется, это общая проблема, с которой люди сталкиваются.

Один из способов, которые я делал раньше, - это использование оператора распространения ... для создания «нового» массива, используя старый. Таким образом, ссылка отличается и запускает обнаружение изменений для повторного рендеринга с использованием новых значений.

Примерно так:

setNavItemAttribute(...) {
  // code to update the corresponding item in the existing navItems array
  const navItemsCopy = [...this.navItems];

  // use the new array reference to trigger change detection
  this._itemsSubject.next(navItemsCopy);
}

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

...