Есть ли в Angular способ реагировать на изменения модели без использования $watch?

Я реализую простое управление вращением в AngularJS, и я хочу реагировать как на пользовательский ввод, так и на изменения с помощью кнопок +/-. Вот мой HTML:

<input type='button' ng-click="MyProperty = MyProperty - 1" value="-">
<input type='text' ng-model="MyProperty" ng-change="log('changed from ngChange')">
<input type='button' ng-click="MyProperty = MyProperty + 1" value="+">

Но это будет отслеживать только "пользовательские изменения", насколько ngChange поддерживает только обновления взаимодействия с пользователем в соответствии с документацией

Так что теперь я смотрю на $scope.$watch как Фредерик рекомендует:

$scope.$watch('MyProperty', function() {
  $scope.log('changed from $watch');
});

Смотрите демо плункер

Но это кажется неправильным.

  1. Во-первых, это не декларативно, и вы должны искать код MyTestProperty найти эту привязку.
  2. Если хотите, хотели бы разместить $scope.log в отдельной модели вы должны либо ввести $scope или сделать привязку в контроллере. И, насколько я понимаю, оба способа не считаются лучшими практиками.

Некоторые люди думают, что $watch это вообще плохо по ряду других причин. Но решение посоветовал там (который будет вызывать log в ngClick напрямую) не имеет большого значения для меня. По сути, вы должны вручную отслеживать все изменения, и если приходит новый актер, вы должны скопировать туда свою логику.

Таким образом, вопросы будут: есть ли способ, который позволяет автоматически отслеживать обновления модели без $watch? И насколько плоха идея реализовать собственное средство для этого, если есть сейчас такой способ?

3 ответа

Решение

Есть несколько способов сделать это, но самый элегантный способ требует ngModel ввода, а затем использовать его для просмотра / манипулирования значением.

Вот ваш обновленный Plunker.

.directive('outputIt', function() {
    return {
      restrict: 'A',
      scope: {
        outputIt: '&'
      },
      require: '?ngModel',
      link: function(scope, element, attrs, ngModelCtrl) {
        ngModelCtrl.$parsers.push(function(val) {
          //console.log("the model was changed by input")
          scope.outputIt()(val);
        });
        ngModelCtrl.$formatters.push(function(viewValue) {
          //console.log("the model was changed from outside.");
          scope.outputIt()(viewValue);
          return viewValue;
        });
      }
    }
  })


Чтобы узнать больше об этом, вот очень классная статья об этом: Atricle

Удачи!

Вы смотрели в сторону ES5? По сути, это нативная функциональность $watch в javascript. Разница в том, что вы сохраняете функции set/get инкапсулированными со свойством, тогда как $watch может применяться снаружи где угодно.

var MyCtrl = function(){
  this._selectedItem = null;
};

Object.defineProperty(MyCtrl.prototype, "selectedItem", {
    get: function () {
        return this._selectedItem;
    },
    set: function (newValue) {
        this._selectedItem = newValue;

        //Call method on update
        this.onSelectedItemChange(this._selectedItem);
    },
    enumerable: true,
    configurable: true
});

Конечно использовать:

<input type="number" />

Это создаст вам спиннер =)

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