Angularjs setValidity导致modelValue不更新

时间:2014-07-11 07:56:48

标签: angularjs validation angularjs-directive

我对表单有一些基本的麻烦。这就是我的所作所为。

我从这里抓住了这个看起来很酷的指令:https://github.com/TheSharpieOne/angular-input-match

看起来像这样:

directive('match', function () {
    return {
      require: 'ngModel',
      restrict: 'A',
      scope: {
        match: '='
      },
      link: function(scope, elem, attrs, ngModel) {
        scope.$watch(function() {
          return (ngModel.$pristine && angular.isUndefined(ngModel.$modelValue)) || scope.match === ngModel.$viewValue;
        }, function(currentValue, previousValue) {
          ngModel.$setValidity('match', currentValue);
        });
      }
    };
  });

本质上,该指令监视它附加到模型值的元素,并将其与match属性中的模型值进行比较。

所以...例如,下面我们正在观察两个密码是否匹配:

Password: <input ng-model="password" type="password" />
Confirm: <input ng-model="passwordConfirm" type="password" match="password" />

该指令似乎正在起作用,因为它适当地设置了ng-valid-match和ng-invalid-match。

但是,一旦设置为无效,passwordConfirm模型就不会再次更新。我已经完成了大量的console.loggin,在指令中查看了ngModel,这是两个密码匹配时的样子:

Constructor {$viewValue: "asdf", $modelValue: undefined, $validators: Object, $parsers: Array[0], $formatters: Array[0]…}
$$debounceViewValueCommit: function (trigger, revalidate) {
$$invalidModelValue: "asdf"
$$lastCommittedViewValue: "asdf"
$$runValidators: function (modelValue, viewValue) {
$$validityState: ValidityState
$$writeModelToScope: function () {
$commitViewValue: function (revalidate) {
$dirty: true
$error: Object
$formatters: Array[0]
$invalid: false
$isEmpty: function (value) {
$modelValue: undefined
$name: "passwordConfirmation"
$parsers: Array[0]
$pristine: false
$render: function () {
$rollbackViewValue: function () {
$setPristine: function () {
$setTouched: function () {
$setUntouched: function () {
$setValidity: function (validationErrorKey, isValid) {
$setViewValue: function (value, trigger, revalidate) {
$touched: true
$untouched: false
$valid: true
$validate: function () {
$validators: Object
$viewChangeListeners: Array[0]
$viewValue: "asdf"
__proto__: Object

请注意$ viewValue是正确的,但$ modelValue列为undefined,$ invalidModelValue仍然有值。

这里有html的样子,当两个密码匹配时再次显示:

<input type="password" class="form-control ng-isolate-scope ng-dirty ng-valid-required ng-valid ng-valid-match ng-touched" id="passwordConfirmation" name="passwordConfirmation" placeholder="Confirm your password" ng-model="passwordConfirmation" required="" match="password" style="">

我错过了什么地方吗?我已经圈了几个小时。

3 个答案:

答案 0 :(得分:6)

recent update中,根据字段的有效性对$modelValue的填充方式进行了更改。如果该字段无效,则$modelValue将设置为undefined,并且将使用该值填充新属性$$invalidModelValue

作为使用1.2。*和1.3。*的解决方案,我想出了这个:

 .directive('match', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
        scope: {
            match: '='
        },
        link: function(scope, elem, attrs, ctrl) {
            scope.$watch(function() {
                modelValue = ctrl.$modelValue || ctrl.$$invalidModelValue;
                return (ctrl.$pristine && angular.isUndefined(modelValue)) || scope.match === modelValue;
            }, function(currentValue) {
                ctrl.$setValidity('match', currentValue);
            });
        }
    };
});

Plunkr

虽然此解决方案适用于两个版本,但1.3。*具有新版$validators管道,建议用于新版本。

答案 1 :(得分:3)

看起来好像使用$ setValidity不是这里的方法。我发现this question提出了一个不同的解决方案,使用$ validators和$ validate(),这对我很有帮助。新代码如下所示:

directive('match', function () {
  return {
    require: 'ngModel',
    restrict: 'A',
    scope: {
      match: '='
    },
    link: function(scope, elem, attrs, ngModel) {
      scope.$watch('match', function(pass){
        ngModel.$validate();
      });
      ngModel.$validators.match = function(modelValue, viewValue){
        var value = modelValue || viewValue;
        var match = scope.match;
        return value === match;
      };
    }
  };
});

答案 2 :(得分:1)

可能与您使用

的事实有关
scope: { 
     match : "=" 
}

为您的指令创建一个独立的范围,并且不是来自您的ngModel所在的父范围的herite。

我建议您尝试删除指令的范围部分,然后从属性中访问它。

它会变成:

directive('match', function () {
    return {
      require: 'ngModel',
      restrict: 'A',
      link: function(scope, elem, attrs, ngModel) {
        scope.match = attrs.match;
        scope.$watch(function() {
          return (ngModel.$pristine && angular.isUndefined(ngModel.$modelValue)) || scope.match === ngModel.$viewValue;
        }, function(currentValue, previousValue) {
          ngModel.$setValidity('match', currentValue);
        });
      }
    };
  });