AngularJS 指令未触发更改回调

AngularJS Directive Not Firing On-Change Callback

我已经创建了一个用于 CSS 样式的数字步进器,但是在手动输入时无法触发 ng-change

我在 plunker 上创建了一个日志来说明何时触发回调。正如你在玩它时看到的那样,当你点击步进箭头时它工作正常,但当你直接在框中输入时就不行了。

当前代码示例:Plunker

HTML:

<div class="stepper-container">
    <input type="text" ng-model="ngModel">
    <button class="stepper-up fa fa-chevron-up" ng-click="increment()"></button>
    <button class="stepper-down fa fa-chevron-down" ng-click="decrement()"></button>
</div>

JavaScript:

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

app.controller('MainCtrl', function($scope) {
  $scope.myModel = null;
  $scope.log = [];

  $scope.someMethod = function () {
    $scope.log.push('Change event on ' + $scope.myModel);
  }
});

app.directive('numericStepper', function () {
  return {
    restrict: 'EA',
    require: 'ngModel',
    scope: {
      ngModel: '='
    },
    replace: true,
    templateUrl: 'numeric-stepper.html',
    link: function (scope, element, attrs, ngModelCtrl) {
      console.log('NumericStepper::link', ngModelCtrl.$viewValue);

      var sizingUnit = null;
      var css3Lengths = [
        // Percentage
        '%',
        // Font Relative
        'em', 'ex', 'ch', 'rem',
        // Viewport Relative
        'vw', 'vh', 'vmin', 'vmax',
        // Absolute
        'cm', 'mm', 'in', 'px', 'pt', 'pc'
      ];

      scope.$watch(function () {
        return ngModelCtrl.$modelValue;
      }, function (newValue, oldValue) {
        updateValue();
      });

      ngModelCtrl.$formatters.unshift(function (value) {
        value = isNaN(parseInt(value)) ? 0 : value;
        return value;
      });

      scope.increment = function () {
        updateValue(1)
      };

      scope.decrement = function () {
        updateValue(-1);
      };

      function updateValue(amount) {
        var matches = ngModelCtrl.$viewValue.toString().split(/(-?\d+)/);
        var value = (parseInt(matches[1]) || 0) + (amount || 0);
        sizingUnit = matches[2].trim();

        ngModelCtrl.$setViewValue(value + sizingUnit);
        sanityCheck();
      }

      function sanityCheck() {
        var validity = css3Lengths.indexOf(sizingUnit) != -1;
        ngModelCtrl.$setValidity('invalidUnits', validity);
      }
    }

} });

更改您的模板文本框以包含对 ngChange 的隔离范围调用。在该函数中,使用超时允许模型 update/digest 在调用父控制器更改函数之前发生...

所以更改模板文本框:

<input type="text" ng-model="ngModel" ng-change="textChanged()">

然后更改您的指令:

// $timeout works better here than watch
app.directive('numericStepper', function ($timeout) { 

  return {
    restrict: 'EA',
    require: 'ngModel',
    scope: {
      ngModel: '=',
      ngChange: '&' // add me!
    },
    replace: true,
    templateUrl: 'numeric-stepper.html',
    link: function (scope, element, attrs, ngModelCtrl) {
      console.log('NumericStepper::link', ngModelCtrl.$viewValue);

      var sizingUnit = null;
      var css3Lengths = [
        // Percentage
        '%',
        // Font Relative
        'em', 'ex', 'ch', 'rem',
        // Viewport Relative
        'vw', 'vh', 'vmin', 'vmax',
        // Absolute
        'cm', 'mm', 'in', 'px', 'pt', 'pc'
      ];
      /********** DONT NEED THIS
      // scope.$watch(function () {
      //   return ngModelCtrl.$modelValue;
      // }, function (newValue, oldValue) {
      //   updateValue();
      // });
      ******************/

      // Add this function
      scope.textChanged = function() { 
        $timeout(function(){ 
          updateValue();
          scope.ngChange(); }, 500); // could be lower
      }
      ngModelCtrl.$formatters.unshift(function (value) {
        value = isNaN(parseInt(value)) ? 0 : value;
        return value;
      });

      scope.increment = function () {
        updateValue(1)
      };

      scope.decrement = function () {
        updateValue(-1);
      };

      function updateValue(amount) {
        var matches = ngModelCtrl.$viewValue.toString().split(/(-?\d+)/);
        var value = (parseInt(matches[1]) || 0) + (amount || 0);
        sizingUnit = matches[2].trim();

        ngModelCtrl.$setViewValue(value + sizingUnit);
        sanityCheck();
      }

      function sanityCheck() {
        var validity = css3Lengths.indexOf(sizingUnit) != -1;
        ngModelCtrl.$setValidity('invalidUnits', validity);
      }
    }
  }
});

和工作 plunker

补充@doog 已经说过的内容

您可以将 $timeout 间隔设置为 0,这样效果一样

scope.textChanged = function() { 
    $timeout(function(){ 
      updateValue();
      scope.ngChange(); }, 0); // could be Zero