Search code examples
angularjsangular-ngmodel

Angular ngChange handler gets old value


I am creating angular directive, which wraps html input with bootstrap form group. I use ng-change event to listen to changes, but I get old value inside ng-change handler.To show this, I created to identical directives, one uses ng-keyup and another uses ng-change event to listen to changes.

var app = angular.module('app', []);

app.controller('home', function() {
  this.textKeyUp = 'KeyUp';
  this.textNgChange = 'NgChange';
  
  this.textKeyUpChanged = function() {
    console.log('Changed on KeyUp:', this.textKeyUp);
  };
  
  this.textNgChangeChanged = function() {
    console.log('Changed on NgChange:', this.textNgChange);
  };
});

app.directive('apTextKeyUp', function() {
  return {
    controller: function() {},
    controllerAs: 'ctrl',
    bindToController: {
      model: '=',
      change: '&'
    },
    scope: {},
    template: '<input ng-model="ctrl.model" ng-keyup="ctrl.change()" />'
  };
});


app.directive('apTextNgChange', function() {
  return {
    controller: function() {},
    controllerAs: 'ctrl',
    bindToController: {
      model: '=',
      change: '&'
    },
    scope: {},
    template: '<input ng-model="ctrl.model" ng-change="ctrl.change()" />'
  };
});
<!DOCTYPE html>
<html ng-app="app">
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
  </head>
  <body ng-controller="home as ctrl">
    <h3>KeyUp</h3>
    <ap-text-key-up model="ctrl.textKeyUp" change="ctrl.textKeyUpChanged()"></ap-text-key-up>
    
    <h3>NgChange</h3>
    <ap-text-ng-change model="ctrl.textNgChange" change="ctrl.textNgChangeChanged()"></ap-text-ng-change>
  </body>
</html>

Both directives update model value, but inside textNgChangeChanged handler value is not updated yet.

Is this by design? How can I fix this problem?


Solution

  • It looks like when using Primative type as the ng-model value, ng-change will be invoke when the value is changed. Since this is a primative type, the value hasn't been propagated to the homeCtroller. If you pass the directive a reference object, things work as expected.

    passing text by reference

    var app = angular.module('app', []);
    
    app.controller('home', function() {
      this.text = {val: 'ABC'};
      this.textChanged = function() {
        console.log('Changed:', this.text.val);
      };
    });
    
    app.directive('apText', function() {
      return {
        controller: function() {
          var vm = this;
          vm.onChange = function(){
            console.log(vm.model);
            vm.change();
          };
        },
        controllerAs: 'ctrl',
        bindToController: {
          model: '=',
          change: '&'
        },
        scope: true,
        template: '<input ng-model="ctrl.model.val" ng-change="ctrl.onChange()" />'
      };
    });
    <!DOCTYPE html>
    <html ng-app="app">
    
      <head>
        <link rel="stylesheet" href="style.css" />
        <script src="https://code.angularjs.org/1.5.0-rc.0/angular.js"></script>
        <script src="script.js"></script>
      </head>
    
      <body ng-controller="home as ctrl">
        <ap-text model="ctrl.text" change="ctrl.textChanged()"></ap-text>
      </body>
    
    </html>

    passing text by val

    var app = angular.module('app', []);
    
    app.controller('home', function() {
      this.text = 'ABC';
      this.textChanged = function() {
    console.log('Changed:', this.text);
      };
    });
    
    app.directive('apText', function() {
      return {
    controller: function() {
      var vm = this;
      vm.onChange = function(){
        console.log(vm.model);
        vm.change();
      };
    },
    controllerAs: 'ctrl',
    bindToController: {
      model: '=',
      change: '&'
    },
    scope: true,
    template: '<input ng-model="ctrl.model" ng-change="ctrl.onChange()" />'
      };
    });
    <!DOCTYPE html>
    <html ng-app="app">
    
      <head>
        <link rel="stylesheet" href="style.css" />
        <script src="https://code.angularjs.org/1.5.0-rc.0/angular.js"></script>
        <script src="script.js"></script>
      </head>
    
      <body ng-controller="home as ctrl">
        <ap-text model="ctrl.text" change="ctrl.textChanged()"></ap-text>
      </body>
    
    </html>