Обнаружение изменения углового2: ngOnИзменяет не стрельбу по вложенному объекту
Я знаю, что я не первый, кто спрашивает об этом, но я не могу найти ответ в предыдущих вопросах. У меня это в одном компоненте
В controllerе rawLapsdata
время от времени мутирует.
В laps
данные выводятся как HTML в табличном формате. Это изменяется при изменении rawLapsdata
.
- Включить подсказку углового ui для пользовательских событий
- Нельзя привязываться к 'ng-forOf', поскольку это не известное свойство
- Обработка 401s по всему миру с помощью углового
- Угловой провайдер для NameService
- Как передать параметры запроса с помощью routerLink в новом Router V 3 alpha (владивосток)
Мой компонент map
должен использовать ngOnChanges
в качестве триггера для перерисовки маркеров на карте Google. Проблема в том, что ngOnChanges не срабатывает, когда rawLapsData
изменяется в родительском. Что я могу сделать?
import {Component, Input, OnInit, OnChanges, SimpleChange} from 'angular2/core'; @Component({ selector: 'map', templateUrl: './components/edMap/edMap.html', styleUrls: ['./components/edMap/edMap.css'] }) export class MapCmp implements OnInit, OnChanges { @Input() lapsData: any; map: google.maps.Map; ngOnInit() { ... } ngOnChanges(changes: { [propName: string]: SimpleChange }) { console.log('ngOnChanges = ', changes['lapsData']); if (this.map) this.drawMarkers(); }
Обновление: ngOnChanges не работает, но похоже, что lapsData обновляется. В ngInit есть прослушиватель событий для изменения масштаба, который также вызывает this.drawmarkers
. Когда я меняю масштаб, я действительно вижу изменение маркеров. Поэтому единственная проблема заключается в том, что я не получаю уведомление во время изменения входных данных.
У родителя у меня есть эта строка. (Напомним, что это изменение отражается в кругах, но не на карте).
this.rawLapsData = deletePoints(this.rawLapsData, this.selectedTps);
И обратите внимание, что this.rawLapsData
сам по себе является указателем на середину большого json-объекта
this.rawLapsData = this.main.data.TrainingCenterDatabase.Activities[0].Activity[0].Lap;
- * ngIf и * ngFor для одного элемента, вызывающего ошибку
- Угловая 2 внутренняяHTML (щелчок) привязка
- Angularjs препятствуют отправке формы при неудачной проверке ввода
- Angular2: приложение аварийно завершает работу / становится невосприимчивым после обнаружения исключения / ошибки
- Строки таблицы Angular2 как компонент
- Как использовать * ngIf else в Angular?
- Запрос Firebase, если дочерний элемент дочернего элемента содержит значение
- Угловой валидатор, который опирается на несколько полей формы
rawLapsData
продолжает указывать на тот же массив, даже если вы изменяете содержимое массива (например, добавляете элементы, удаляете элементы, изменяете элемент).
При обнаружении изменений, когда Angular проверяет свойства компонентов компонентов для изменения, он использует (по существу) ===
для грязной проверки. Для массивов это означает, что ссылки на массив (только) грязно проверены. Поскольку rawLapsData
массив rawLapsData
не изменяется, ngOnChanges()
не будет вызываться.
Я могу представить два возможных решения:
-
ngDoCheck()
и выполните собственную логику обнаружения изменений, чтобы определить, изменилось ли содержимое массива. ( Например, пример документа Lifecycle Hooks.) -
Назначьте новый массив
rawLapsData
всякий раз, когда вы вносите какие-либо изменения в содержимое массива. ЗатемngOnChanges()
, потому что массив (ссылка) появится как изменение.
В вашем ответе вы пришли к другому решению.
Повторение некоторых комментариев здесь на OP:
Я все еще не вижу, как
laps
могут подхватить изменение (возможно, он должен использовать что-то, что соответствует самомуngOnChanges()
?), Аmap
не может.
- В компоненте
laps
ваш код / шаблонlapsData
каждую запись в массивеlapsData
и отображает содержимое, поэтому на каждой части данных отображаются угловые привязки. - Даже когда Angular не обнаруживает никаких изменений в свойствах ввода компонента (используя проверку
===
), он по-прежнему (по умолчанию) проверяет все привязки шаблонов. Когда какое-либо из этих изменений изменится, Angular обновит DOM. Это то, что вы видите. - Компонент
maps
вероятно, не имеет привязок в своем шаблоне кlapsData
вводаlapsData
, не так ли? Это объясняет разницу.
Обратите внимание, что lapsData
в обоих компонентах и rawLapsData
в родительском компоненте все указывают на один и тот же / один массив. Таким образом, хотя Angular не замечает никаких (ссылочных) изменений в lapsData
ввода lapsData
, компоненты «get» / видят содержимое любого массива, потому что все они разделяют / ссылаются на один массив. Нам не нужен Angular для распространения этих изменений, как и с примитивным типом (string, number, boolean). Но с примитивным типом любое изменение значения всегда ngOnChanges()
бы ngOnChanges()
– это то, что вы используете в своем ответе / решении.
Как вы, вероятно, уже выяснили, что свойства ввода объекта имеют то же поведение, что и свойства ввода массива.
Не самый чистый подход, но вы можете просто клонировать объект каждый раз, когда вы меняете значение?
rawLapsData = Object.assign({}, rawLapsData);
Я думаю, что я предпочел бы этот подход для реализации своего собственного ngDoCheck()
но, возможно, кому-то понравится @ GünterZöchbauer.
В качестве дополнения к второму решению Марка Райкока
Назначьте новый массив rawLapsData всякий раз, когда вы вносите какие-либо изменения в содержимое массива. Затем вызывается ngOnChanges (), потому что массив (ссылка) появится как изменение
вы можете клонировать содержимое массива следующим образом:
rawLapsData = rawLapsData.slice(0);
Я упоминаю это, потому что
rawLapsData = Object.assign ({}, rawLapsData);
не работал для меня. Надеюсь, это поможет.
Если данные поступают из внешней библиотеки, вам может потребоваться запустить инструкцию zone.run(...)
данных в zone.run(...)
. Инъекционная zone: NgZone
для этого. Если вы можете запустить экземпляр внешней библиотеки в zone.run()
уже, вам может не понадобиться zone.run()
позже.
Мое решение «взломать»
selectedTps изменяется одновременно с rawLapsData, и это дает другой шанс обнаружить изменение через более простой примитивный тип объекта . Он НЕ элегантный, но он работает.
Вот хак, который просто избавил меня от неприятностей с этим.
Таким образом, аналогичный сценарий для OP – у меня есть вложенный угловой компонент, которому нужны данные, переданные ему, но входные точки к массиву, и, как упоминалось выше, Angular не видит изменений, так как не рассматривает содержимое массива.
Поэтому, чтобы исправить это, я преобразовываю массив в строку для Angular, чтобы обнаружить изменение, а затем во вложенном компоненте я снова разбиваю строку (‘,’) на массив и его счастливые дни.
Используйте ChangeDetectorRef.detectChanges()
чтобы сообщить Angular, чтобы запустить обнаружение изменений, когда вы редактируете вложенный объект (который он пропускает с его грязной проверкой).
В случае массивов вы можете сделать это следующим образом:
В файле .ts
(родительский компонент), в котором вы обновляете rawLapsData
выполните следующие действия:
rawLapsData = somevalue; // change detection will not happen
Решение:
rawLapsData = {...somevalue}; //change detection will happen
и ngOnChanges
в дочернем компоненте
Обнаружение изменений не срабатывает при изменении свойства объекта (включая вложенный объект). Одним из решений было бы переназначить новую ссылку на объект с помощью функции lodash clone ().
import * as _ from 'lodash'; this.foo = _.clone(this.foo);