当数组更改时,不会调用AngularJs Formatter

时间:2016-01-23 11:41:54

标签: javascript arrays angularjs angular-ngmodel formatter

在我的示例代码中,我有两个我无法理解的行为,我希望你们可以告诉我发生了什么以及我做错了什么:

  1. 我在指令array-to-string中只有一个格式化程序 在初始化过程中被调用。当我更新 在我的控制器中使用push函数的格式化格式化程序不是 被调用。为什么这样做,我该怎么做才能克服这个问题?
  2. 在array-to-string的formatter函数中,我将数组的值提取为单个字符串。转换后,我返回该字符串。现在为什么不将mydir的ng-model更改为array-to-string的格式化器返回的值?这对我来说没有意义,因为mydir的格式化程序得到了数组到字符串格式化程序的结果(我误用了mydir指令中的格式化程序来显示我认为哪些内容出错了)
  3. angular.module("app", []);
    
    angular.module("app").directive("arrayToString", function() {
      return {
        restrict: "A",
        require: "?ngModel",
        link: function(scope, element, attrs, ngModelCtrl) {
          ngModelCtrl.$formatters.push(function(array) {
            console.log("formatter called");
            var text = "";
            angular.forEach(array, function(e) {
              text += e.label + ", ";
            });
            text = text.substring(0, text.length - 2);
            console.log(text);
            return text;
          });
        }
      };
    });
    
    angular.module("app").directive("mydir", function() {
      return {
        restrict: "E",
        scope: {
          ngModel: "="
        },
        require: "?ngModel",
        link: function(scope, element, attrs, ngModelCtrl) {
          ngModelCtrl.$formatters.push(function(alreadyFormattedValue) {
            scope.myString = alreadyFormattedValue;
          });
        },
        template: "<div>{{myString}}</div><div>{{ngModel}}</div>"
      }
    });
    
    angular.module("app").controller("Controller", function() {
      var _this = this;
      
      _this.myValues = [
        { label: "apple" },
        { label: "lemon" },
        { label: "pear" }
      ];
      
      _this.add = function() {
        _this.myValues.push({ label: "strawberry" });
      }
    });
    <!DOCTYPE html>
    <html>
    
      <head>
        <script src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8" data-require="angular.js@1.3.20"></script>
        <link href="style.css" rel="stylesheet" />
        <script src="script.js"></script>
      </head>
    
      <body ng-app="app" ng-controller="Controller as ctrl">
        <mydir ng-model="ctrl.myValues" array-to-string=""></mydir>
        <button type="button" ng-click="ctrl.add()">Add</button>
      </body>
    
    </html>

1 个答案:

答案 0 :(得分:0)

如果对ngModel指令的源代码更深入,我们可以找到使用custom function的监视器。在此函数中,if存在下一个条件。

if (modelValue !== ctrl.$modelValue &&
       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
    )

所以,modelValue只是与!==进行了比较,如果引用未更改,则viewValue既不应用格式化程序,也不应用验证程序。

最简单的方法 - 只需更改引用,即使用concat函数代替push

_this.myValues = _this.myValues.concat({ label: "strawberry" });

样品

&#13;
&#13;
angular.module("app", []);

angular.module("app").directive("arrayToString", function() {
  return {
    restrict: "A",
    require: "?ngModel",
    link: function(scope, element, attrs, ngModelCtrl) {
      ngModelCtrl.$formatters.push(function(array) {
        console.log("formatter called");
        var text = "";
        angular.forEach(array, function(e) {
          text += e.label + ", ";
        });
        text = text.substring(0, text.length - 2);
        console.log(text);
        return text;
      });
    }
  };
});

angular.module("app").directive("mydir", function() {
  return {
    restrict: "E",
    scope: {
      ngModel: "="
    },
    require: "?ngModel",
    link: function(scope, element, attrs, ngModelCtrl) {
      ngModelCtrl.$formatters.push(function(alreadyFormattedValue) {
        scope.myString = alreadyFormattedValue;
      });
    },
    template: "<div>{{myString}}</div><div>{{ngModel}}</div>"
  }
});

angular.module("app").controller("Controller", function() {
  var _this = this;
  
  _this.myValues = [
    { label: "apple" },
    { label: "lemon" },
    { label: "pear" }
  ];
  
  _this.add = function() {
    _this.myValues = _this.myValues.concat({ label: "strawberry" });
  }
});
&#13;
<!DOCTYPE html>
<html>

  <head>
    <script src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8" data-require="angular.js@1.3.20"></script>
    <link href="style.css" rel="stylesheet" />
    <script src="script.js"></script>
  </head>

  <body ng-app="app" ng-controller="Controller as ctrl">
    <mydir ng-model="ctrl.myValues" array-to-string=""></mydir>
    <button type="button" ng-click="ctrl.add()">Add</button>
  </body>

</html>
&#13;
&#13;
&#13;

下一个变体:您可以添加自己的$watch并手动应用格式化程序

scope.$watchCollection(function() {
    return ngModelCtrl.$modelValue;
}, function(n) {
    var formatters = ngModelCtrl.$formatters,
        idx = formatters.length;

    var viewValue = n;
    while (idx--) {
        viewValue = formatters[idx](viewValue);
    }
    ngModelCtrl.$setViewValue(viewValue);
});

样品:

&#13;
&#13;
angular.module("app", []);

angular.module("app").directive("arrayToString", function() {
  return {
    restrict: "A",
    require: "?ngModel",
    link: function(scope, element, attrs, ngModelCtrl) {
      scope.$watchCollection(function() {
        return ngModelCtrl.$modelValue;
      }, function(n) {
        var formatters = ngModelCtrl.$formatters,
          idx = formatters.length;

        var viewValue = n;
        while (idx--) {
          viewValue = formatters[idx](viewValue);
        }
        ngModelCtrl.$setViewValue(viewValue);
      });
      ngModelCtrl.$formatters.push(function(array) {
        console.log("formatter called");
        var text = "";
        angular.forEach(array, function(e) {
          text += e.label + ", ";
        });
        text = text.substring(0, text.length - 2);
        console.log(text);
        return text;
      });
    }
  };
});

angular.module("app").directive("mydir", function() {
  return {
    restrict: "E",
    scope: {
      ngModel: "="
    },
    require: "?ngModel",
    link: function(scope, element, attrs, ngModelCtrl) {
      ngModelCtrl.$formatters.push(function(alreadyFormattedValue) {
        scope.myString = alreadyFormattedValue;
      });
    },
    template: "<div>{{myString}}</div><div>{{ngModel}}</div>"
  }
});

angular.module("app").controller("Controller", function() {
  var _this = this;

  _this.myValues = [{
    label: "apple"
  }, {
    label: "lemon"
  }, {
    label: "pear"
  }];

  _this.add = function() {
    _this.myValues.push({
      label: "strawberry"
    });
  }
});
&#13;
<!DOCTYPE html>
<html>

<head>
  <script src="https://code.angularjs.org/1.4.8/angular.js" data-semver="1.4.8" data-require="angular.js@1.3.20"></script>
  <link href="style.css" rel="stylesheet" />
  <script src="script.js"></script>
</head>

<body ng-app="app" ng-controller="Controller as ctrl">
  <mydir ng-model="ctrl.myValues" array-to-string=""></mydir>
  <button type="button" ng-click="ctrl.add()">Add</button>
</body>

</html>
&#13;
&#13;
&#13;