angular-validation icon indicating copy to clipboard operation
angular-validation copied to clipboard

CheckValid not working as expected

Open codeuniquely opened this issue 8 years ago • 3 comments

when i call to check valid

this.checkValid = function(form) {
   return !!(form && form.$valid);
};

Here . form.$valid = undefined

This is because i have an OPTIONAL control (user does not have to fill it in), but if they do it has a regEx validation.

so this input on the form also has a $valid = undefined - because the user NEVER entered the input and just skipped that part of the form.

The control is still ng-valid, the form is ng-valid (all inputs) - and none of the error messages show.

but checkValid returns FALSE - so the form does not submit

codeuniquely avatar Jul 01 '17 07:07 codeuniquely

I'm not sure if I understand your description correctly, do you mind post your <Form> script here so that we can check your requirement.

hueitan avatar Jul 01 '17 11:07 hueitan

I've worked out what the issue is ..

I have added an input like this

<input
    ng-class="{focus:form.optionalNumber.$focus===true}"
    ng-attr-type="text"
    id="optionalNumber"
    name="optionalNumber"
    autocomplete="off"
    pattern="\d*"
    ng-model="model.optionalNumber"
    maxlength="11"
    ng-focus="form.optionalNumber.$focus=true"
    ng-blur="form.optionalNumber.$focus=false"
    initial-validity=true
    validator="optionalNumber"
    invalid-callback="invalidMsg(message)" />

The validator looks like this

optionalNumber: function(value) {
   if(!value || value.length===0) {
     return true;
   }
   var regexp = expr.formattedNumber;
   return regexp.test(value);
},

The button submit calls this function

submit(form, state) {
  let isValid = this.$validationProvider.checkValid(form);
  if (isValid) {
    return this.save(state);
  }
  console.log('Invalid Form');
  this.$scope.form.submitted = true;
  this.scrollToFirstError();
}

The issue is that because that the input is optional - so the user never has to enter a value and can skip the form (so it never moves from $pristine and the $watch never fires because the user does not alter the value. This means that the $valid and $invalid values on the form never fire but the form input is added to the $pending array (for validation) - this means that the $invalid and $valid properties on the form are "undefined"

So to combat this added the initial-validation=true - so that the initial state would be set and if the user did nothing then the input would still be valid. However this does not work.

on line 687 of angular-validation.js you have this code

/**
 * Set initial validity to undefined if no boolean value is transmitted
*/
var initialValidity = void 0;
if (typeof attrs.initialValidity === 'boolean' ) {
    initialValidity = attrs.initialValidity;
} 

The issue here is that typeof attrs.initialValidity ALWAYS === 'string' because you are reading it from attrs so it will never be a boolean, it doesn't matter if you pass

initial-validity=true
initial-validity="true"
initial-validity="{{someConst}}" .   // where the const is true
initial-validity="{{someBinding}}"   // where the binding is to a variable that is true

When you inspect attrs..initialValidity its still a string, so the initial set up of the state is never set and so the optional model property validation remains "undefined" and the check fails.

there is a possible fix

Suggestion:

If you instead add a check for 'true' and 'false' when init initialValidity in your code...

if (typeof attrs.initialValidity === 'boolean' || attrs.initialValidity === 'true' || attrs.initialValidity === 'false') {
    initialValidity = attrs.initialValidity;
}

THEN it will work as expected ....

codeuniquely avatar Jul 01 '17 19:07 codeuniquely

A possible solution I use without forking the project for now is a custom directive that does the initialization of the ngModel field validity.

export const ngModelInitialValidityDirective = ($parse) => {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: (scope, element, attrs, ngModelCtrl) => {
      const initialValidity = $parse(attrs[directiveName])(scope);

      if (_isBoolean(initialValidity)) {
        ngModelCtrl.$setValidity(ngModelCtrl.$name, initialValidity);
      }
    },
  };
};

ngModelInitialValidityDirective.$inject = ['$parse'];

ghost avatar Mar 19 '19 11:03 ghost