Установка углового ввода срабатывает только один раз

Я хочу предварить вопрос, говоря, что я уже прочитал и понял другие потенциально дублирующиеся вопросы (например, этот) и не смогли выполнить эту работу, что заставило меня поверить, что проблема немного отличается по своей природе, или я неправильно понял что-нибудь.

Контекст: у меня есть родительский компонент с двумя дочерними компонентами (боковое меню и основное представление). В боковом меню есть несколько фильтров в виде флажков, привязанных к объекту через [(ngModel)] и инициирующих событие с помощью (ngModelChange)="changeFilter()" в шаблоне.

В родительском компоненте ловлю событие: (filter)="updateFilters($event)". Эта функция изменяет общедоступную переменную фильтра, которую я затем передаю другому дочернему компоненту, например: [filterObject]="filters"

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

Проблема: канал вызывается только один раз, после того как я впервые изменил фильтр. Переменная filterObject внутри основного представления отображается правильно обновленной, когда отображается вот так {{filterObject | json}}, но установщик никогда не вызывается. Журналы консоли, помещенные в сеттер и геттер, показывают, что доступ получает только геттер (2-4)

Решения, которые я пробовал:

  • Добавление дополнительного параметра в канал для его запуска. Проблема с этим подходом заключается в том, что установка новой даты должна быть выполнена из установщика для filterObject, который не вызывается - как указано выше.

  • Сделать канал нечистым (работает, но не очень хорошее решение, так как заставляет обрабатывать очень большие массивы сложных объектов 2-4 раза за клик)

  • Заставить родителя обновить переменную фильтра не через оператор присваивания, а через Object.assign, чтобы изменить ссылку и потенциально вызвать установщик

Решения, которые я рассматриваю: Если мне не удастся выяснить, в чем проблема, я подумываю о создании службы для связи между двумя компонентами через наблюдателей. В противном случае мне, возможно, придется согласиться на нечистый канал с некоторым встроенным обнаружением изменений (я думаю о преобразовании объекта фильтра в строку JSON и проверке длины).

боковое меню-component.html

<input type="checkbox" id="filter-type-2" name="filter-type-2" [(ngModel)]="filterObject.type.me" (ngModelChange)="changeFilter()">

боковое меню-component.ts

 @Output() filter = new EventEmitter();
 public filterObject = {
    type: {
        mo: true,
        me: true,
        ar: true,
        cto: true,
        gl: true,
        dl: true
    },
    status: {
        unreviewed: true,
        approved: true,
        other: true
    }
};

// ...

changeFilter() {
    this.filter.emit(this.filterObject);
}

родительский-компонент.html

<aside>
     <app-article-view-aside [article]="article" (close)="closeModal($event)" (filter)="updateFilters($event)"></app-article-view-aside>
</aside>
<div class="articleContainerMainView">
     <app-article-view-main [article]="article" [filterObject]="filters"></app-article-view-main>
</div>

родительский-component.ts

updateFilters(newFilter) {
    this.filters = newFilter;
}

main-view-component.html

<app-finding
        *ngFor="let finding of findings | findingsFilter:filterDate:filterObject; let i = index"
        [finding]="finding"></app-finding>

main-view-component.ts

@Input() set filterObject(filterObject) {
    console.log('setter');
    this._filterObject = filterObject;
    this.filterDate = Date.now();
}

get filterObject() {
    console.log('getter');
    return this._filterObject;
}

private _filterObject = {};

public filterDate = Date.now();

вывод в консоль при первом отображении страницы:

setter
getter
getter
getter
getter

Я действительно хочу сделать это «Угловой путь», поэтому мой вопрос к вам: что мне здесь не хватает?


person Tudor Merlas    schedule 10.07.2018    source источник


Ответы (3)


Возможно, ваш компонент бокового меню испускает тот же объект. Его содержимое может быть изменено, но испускаемый объект остается тем же. Вместо этого рассмотрите возможность создания копии объекта. Object.assign({}, this.filterObject) может вам помочь.

person Sergey P. aka azure    schedule 10.07.2018
comment
Вау, такое простое решение такой сложной проблемы. Я пытался использовать Object.assign, но на родительском компоненте, а не на исходном компоненте. - person Tudor Merlas; 10.07.2018

Это связано с обнаружением изменений вашего жизненного цикла. Без минимального, полного и проверяемого примера я не могу сказать вам, как это решить, но я определенно могу сказать вам, как обойти это.

Начните с того, что дайте второму компоненту переменную шаблона и сообщите своему выводу, чтобы обновить данные в нем:

<aside>
     <app-article-view-aside [article]="article" (close)="closeModal($event)" (filter)="mainView.updateData($event)"></app-article-view-aside>
</aside>
<div class="articleContainerMainView">
     <app-article-view-main #mainView [article]="article" [filterObject]="filters"></app-article-view-main>
</div>

Во втором компоненте вместо использования канала обновите значение в функции:

updateData(event) {
  // original data is the unfiltered array of data
  this.findings = this.originalData.filter(...);
}

И в вашем шаблоне это становится

<app-finding
    *ngFor="let finding of findings; let i = index"
    [finding]="finding"></app-finding>
person Community    schedule 10.07.2018

Проблема в том, что когда вы проходите фильтр в первый раз, вы даете ему ссылку. Таким образом, каждый раз, когда вы меняете его, обнаружение изменений angular никогда не обнаружит его, потому что вы не изменили объект фильтра или ссылку.

Решение: Я думаю, у вас есть два решения: 1. Каждый раз, когда вы обновляете фильтр, меняйте его ссылку, копируя ее. 2. Реализуйте ngDoCheck в своем канале, если я не ошибаюсь, он обнаружит это изменение, даже если ваша ссылка остается прежней.

person Nour    schedule 10.07.2018