Is there a way in Angular to react on model changes without using $watch?

I'm implementing a simple spinner control in AngularJS and I want to react both on user input and changes from +/- buttons. Here is my 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="+">

But this will track only 'user-changes' far as ngChange supports only user-interaction updates as per documentaiton

So now I'm looking at $scope.$watch as Frederik recommends:

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

See plunker demo

But this doesn't seem right enogh.

  1. First it's not declarative and you have to search the code for MyTestProperty to find this binding.
  2. If you want would like to place $scope.log in a separate Model you have to either inject $scope or to do the binding in controller. And as far as I understand both ways are not considered to be the best practicies.

Some people think that $watch is a bad thing in general for a number of other reasons. But the solution advised there (which would be calling log in ngClick directly) doesn't make too much diference to me. Basicly you have to manually track all the changes and if new actor comes you have to copy your logic there.

So the questions would be: is there a way that allows you to automaticly keep track of model updates without $watch? And how bad is the idea to implement your own derective for this if there is now such way?

Answers:

Answer

There are couple of ways to do this, but the most elegant way is requiring the ngModel of the input and then use it to view / manipulate the value.

Here is your updated 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;
        });
      }
    }
  })


To find out more about it, here is a very cool article about it: Atricle

Good luck!

Answer

Have you looked into the ES5 way? It's essentially javascript's native $watch functionality. The difference is you keep the set/get functions are encapsulated with the property, whereas a $watch can be applied externally anywhere.

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
});
Answer

Of course use:

<input type="number" />

This will create you spinner =)

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.