AngularJS 1.5.x $onChanges не работает с изменениями односторонней привязки
Я не понимаю, почему $ onChanges не запускается, когда я изменяю связанный примитив на входе. Может кто-то увидеть, что я сделал неправильно, и объяснить это несложно? Я сделал план быстрого тестового приложения после того, как не смог заставить его работать в моем реальном приложении.
angular
.module('test', [])
.component('test', {
template: '<child application="vm.application"></child>',
controller: 'testCtrl as vm'
})
.controller('testCtrl', function() {
var vm = this;
vm.$onInit = function () {
vm.application = {
data: {
name: 'Test'
}
}
};
})
.component('child', {
template: '<input type="text" ng-model="vm.application.data.name">',
bindings: {
application: '<'
},
controller: 'childCtrl as vm'
})
.controller('childCtrl', function() {
var vm = this;
vm.$onChanges = function (changes) {
console.log('CHANGED: ', changes);
};
})
4 ответа
$onChanges
Метод не вызывается для изменений в свойствах объекта. Изменения по умолчанию для объектов обычно следуют этой последовательности в течение времени жизни компонентов:
- UNINITIALIZED_VALUE для
undefined
undefined
в{}
или же{ someAttribute: someValue, .. }
- (
{..}
вundefined
если тыdelete
объект в родительской области видимости)
Для просмотра субпредприятий вы можете использовать $doCheck
метод, который был добавлен в 1.5.8. Он вызывается в каждом цикле дайджеста и не принимает параметров. С большой властью приходит большая ответственность. В этом методе вы бы поместили логику, которая определяет, было ли обновлено определенное значение или нет - новое значение уже будет обновлено в области видимости контроллера, вам просто нужно найти способ определить, изменилось ли значение по сравнению с ранее известным значением,
Вы могли бы установить previousValueOfObjectAttribute
переменная в контроллере, прежде чем вы начнете ожидать изменения этого конкретного атрибута (например, когда подкомпонент B вызывает output binding
функция в компоненте A, на основе которой изменяется целевой объект - который является входной привязкой к B - в A). В тех случаях, когда это непредсказуемо, когда изменение должно произойти, вы можете сделать копию определенных атрибутов интереса после любого изменения, наблюдаемого через $doCheck
метод.
В моем конкретном случае использования я не проверял явно между старым и новым значением, но использовал обещание (хранилище $q.defer().promise
) с намерением, чтобы любое изменение, которое я бы "успешно" наблюдал в $doCheck
метод разрешит это обещание. Мой контроллер тогда выглядел примерно так:
dn.$doCheck = function () {
if (dn.waitForInputParam &&
dn.waitForInputParam.promise.$$state.status === 0 &&
dn.targetObject.targetAttribute !== false)
dn.waitForInputParam.resolve(dn.targetObject.targetAttribute);
}
dn.listenToInputChange = function () {
dn.waitForInputParam = $q.defer();
dn.waitForInputParam.promise.then(dn.onInputParamChanged);
}
dn.onInputParamChanged = function (value) {
// do stuff
//
// start listening again for input changes -- should be async to prevent infinite $digest loop
setTimeout(dn.listenToInputChange, 1);
}
(WRT promise.$$state.status
см. этот пост).
Для всех остальных целей и задач, следя за изменениями в примитивных типах данных, вы все равно должны использовать $onChanges
, Ссылка: https://docs.angularjs.org/guide/component
Как уже говорилось выше, Angular не отслеживает изменения свойств объекта, однако вы можете заставить Angular полагать, что ваш объект изменяется по ссылке.
Достаточно сделать поверхностную копию объекта, чтобы вызвать событие $onChanges:
vm.campaign = angular.extend({}, vm.campaign);
Кредиты @gkalpak
Имея дело с $onChanges
это сложно. Собственно, поэтому в версии 1.5.8 они представили $doCheck
, аналогично Angular 2 ngDoCheck.
Таким образом, вы можете вручную прослушивать изменения внутри прослушиваемого объекта, что не происходит с $onChanges
hook (вызывается только при изменении ссылки на объект). Это то же самое, но он вызывается для каждого цикла дайджеста, что позволяет вам проверять изменения вручную (но лучше, чем часы).
Для более подробной информации, смотрите этот пост в блоге.