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 и не $onChange,

Кроме того, onChange обновляется только при изменении родительского значения, а не дочернего. Взгляни на этот планкр. Обратите внимание, что console.log срабатывает только при вводе первого ввода.

$onChanges Метод не вызывается для изменений в свойствах объекта. Изменения по умолчанию для объектов обычно следуют этой последовательности в течение времени жизни компонентов:

  1. UNINITIALIZED_VALUE для undefined
  2. undefined в {} или же { someAttribute: someValue, .. }
  3. ({..} в 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 (вызывается только при изменении ссылки на объект). Это то же самое, но он вызывается для каждого цикла дайджеста, что позволяет вам проверять изменения вручную (но лучше, чем часы).

Для более подробной информации, смотрите этот пост в блоге.

Другие вопросы по тегам