我可以使用asyncValidator阻止ng模型更新吗?

时间:2017-03-31 16:20:44

标签: javascript angularjs angular-ngmodel

我有一个应用程序,某些选项不能很好地协同工作。如果用户选择了一对可能导致性能不佳的选项,我们会显示dialog,用户必须同意承认他们要求很多数据,我们可能无法满足要求。

由于对话服务返回了一个可以根据用户想要做的事情来实现或拒绝的承诺,这似乎是在NgModelController上添加异步验证器的好地方。我写了一个plunker来演示场景和我尝试解决方案。

它的内容是:

HTML

<body ng-controller="MainCtrl as ctrl">
  <br>
  Allow chili
  <input type="checkbox" ng-model="ctrl.allowChili">
  <br>
  <select
      ng-model="ctrl.val"
      ng-model-options="{allowInvalid: false}"
      validate-async="ctrl.validateAsync($modelVal)"
      ng-change="ctrl.tellMe()">
    <option value="very">very</option>
    <option value="chili">chili</option>
    <option value="jalepeno">jalepeno</option>
  </select>
  <br>
  <p>The food is {{ctrl.val}} spicy!</p>
</body>

JS

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

myApp.controller('MainCtrl', ['$q', '$scope', function($q, $scope) {
    this.val = 'very';
    this.allowChili = false;

    this.validateAsync = function(modelVal) {
      console.log(modelVal);
      if (modelVal != 'chili' || this.allowChili) {
        console.log('Accepting validation');
        return $q.when();
      } else {
        console.log('Rejecting validation');
        return $q.reject();
      }
    };

    this.tellMe = function() {
      // In my application, this actually issues an HTTP request to get new
      // data and update the view.
      alert('model value changed');
    };
}]);

myApp.directive('validateAsync', ['$q', function($q) {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: linkFn,
  };
  function linkFn(scope, element, attrs, ngModelCtrl) {
    ngModelCtrl.$asyncValidators.myValidator = function(modelVal, viewVal) {
      var args = {
        '$modelVal': modelVal,
        '$viewVal': viewVal,
      };
      return $q.when(scope.$eval(attrs['validateAsync'], args));
    };
  }
}]);

不幸的是,有一些问题。当异步验证器承诺被拒绝时,会发生两件坏事:

  1. 更改听众(例如ng-change)仍然会触发,我的视图仍会更新。
  2. 新模型值似乎为undefined,而我想阻止它发生变化。
  3. NgModelController如何根据承诺的状态阻止提交分阶段变更?

1 个答案:

答案 0 :(得分:0)

这里是plunker of an ugly hack,我认为我可以适应我的用例。更改的内容在validateAsync指令中:

myApp.directive('validateAsync', ['$q', function($q) {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: linkFn,
  };
  function linkFn(scope, element, attrs, ngModelCtrl) {
    var ngModelCtrl$setViewValue = ngModelCtrl.$setViewValue;
    ngModelCtrl.$setViewValue = function(value, trigger) {
      var args = {
        '$val': value,
        '$trigger': trigger,
      };
      $q.when(scope.$eval(attrs['validateAsync'], args)).then(function() {
        ngModelCtrl$setViewValue.call(ngModelCtrl, value, trigger);
      });
    };
  }
}]);

(使用$val而不是$modelVal进行适当的模板更改。

基本上,如果承诺被拒绝,我劫持NgModelController.$setViewValue并且不会调用基本实现。在demo plunker中,您会看到select元素仍然在变化。我相信我的设计师可以做到(如果不是,我有权在我的用例中修复它,因为我实际上并没有使用<select>元素 - 我使用自定义指令来挑选我在更新时完全控制的值。