Angularjs setValidity causing modelValue to not update

梦想的初衷 提交于 2019-12-03 20:12:49

问题


I'm having some basic trouble with a form. Here's what I did.

I snagged this cool looking directive from here: https://github.com/TheSharpieOne/angular-input-match

It looks like this:

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);
        });
      }
    };
  });

Essentially, this directive watches the element it is attached to's model value, and compares it to the model value in the match attribute.

So...for example, below we're watching to see if both passwords match:

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

The directive seems to be working, in that it sets ng-valid-match and ng-invalid-match appropriately.

However, once it is set to invalid, the passwordConfirm model never gets updated again. I've done a ton of console.loggin, looking at ngModel in the directive, and here is what it looks like when both passwords match:

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

Note that the $viewValue is correct, but the $modelValue is listed as undefined and $invalidModelValue still has a value.

Here's what the html looks like, again when both passwords match:

<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="">

Am I missing something somewhere? I've been running in circles for hours.


回答1:


In a recent update, a change was made to the way the $modelValue is populated based on the validity of the field. If the field is invalid, the $modelValue will be set to undefined and a new attribute, $$invalidModelValue will be populated with the value.

As a solution to work with 1.2.* and 1.3.* I have come up with this:

 .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

While this solution works with both version, 1.3.* has the new $validators pipeline which is recommended for the new version.




回答2:


It looks like maybe using $setValidity is not the way to go here. I found this question that proposes a different solution, using $validators and $validate(), and this is working for me great. The new code looks like this:

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;
      };
    }
  };
});



回答3:


Might be related to the fact that you are using

scope: { 
     match : "=" 
}

That create an isolated scope for your directive and doesn't herite from the parent scope where your ngModel is.

I suggest trying to remove that scope part of your directive and access it from attributes instead.

It will become something like :

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);
        });
      }
    };
  });


来源:https://stackoverflow.com/questions/24692775/angularjs-setvalidity-causing-modelvalue-to-not-update

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!