$ Parsers \ $ formatters functions do not work when the browser automatically fills out a form field

I have a specific special verification directive in my application (the code is attached below). The problem is that when one or more form fields are required, and chrome fills them automatically, the fields remain invalid until the user manually changes them. I suspect this is due to the fact that chrome fills the fields to angular even bootstraps.

Is there any way to fix this?

the code:

app.directive('myValidate', function($timeout, $filter) { return { require: 'ngModel', link: function(scope, elm, attrs, ctrl) { var validator = function(viewValue){ var viewValueStr = viewValue + ''; scope.valid = true; scope.fieldName = attrs.name; var nameStr = attrs.name + ''; if(!attrs.displayName || attrs.displayName.length == 0){ // var nameObj = nameStr.split('_'); // for(var i = 0; i < nameObj.length; ++i){ // nameObj[i] = nameObj[i].substr(0, 1).toUpperCase() + nameObj[i].slice(1); // } // var nameStrParsed = nameObj.join(' ');//olde code, working on name, not Hebrew comaptible var nameStrParsed = attrs.placeholder + ''; } else{//data-display-name attribute, the error display is different than the placeholder value var nameStrParsed = attrs.displayName; } scope.fieldErrorDisplay = Boolean(nameStrParsed) ? nameStrParsed : $filter('translate')('THISFIELD'); var valueRequired = scope.$eval(attrs.valueRequired); if(valueRequired && viewValueStr.length == 0 && !attrs.minLength){ scope.valid = false; scope.requirementSpec[nameStr] = [{ 'msg' : scope.fieldErrorDisplay + ' ' + $filter('translate')('ISREQUIRED'), 'class' : undefined }]; } else{ // scope.fieldErrorDisplayObj[nameStr] = scope.fieldErrorDisplay + ' must meet the following requirements: '; scope.requirementSpec[nameStr] = []; if(attrs.minLength){ var itemValidity = viewValue.length >= attrs.minLength; scope.valid = !itemValidity ? false : scope.valid; var item = { 'msg' : $filter('translate')('MINLENGTH', {PARAM: attrs.minLength + ''}), 'class' : itemValidity ? 'valid' : undefined }; scope.requirementSpec[nameStr].push(item); } else if(attrs.valueRequired){ var itemValidity = viewValue && viewValueStr && viewValueStr.length >= 1; scope.valid = !itemValidity ? false : scope.valid; var item = { 'msg' : $filter('translate')('FIELDREQUIRED'), 'class' : itemValidity ? 'valid' : undefined }; scope.requirementSpec[nameStr].push(item); } if(attrs.maxLength){ var itemValidity = viewValue.length <= attrs.maxLength; scope.valid = !itemValidity ? false : scope.valid; var item = { 'msg' : $filter('translate')('MAXLENGTH', {PARAM: attrs.maxLength + ''}), 'class' : itemValidity ? 'valid' : undefined }; scope.requirementSpec[nameStr].push(item); } if(attrs.minLetters){ var itemValidity = (viewValue && /[Az]/.test(viewValue)); scope.valid = !itemValidity ? false : scope.valid; var item = { 'msg' : $filter('translate')('MINLETTERS', {PARAM: attrs.minLetters + ''}), 'class' : itemValidity ? 'valid' : undefined }; scope.requirementSpec[nameStr].push(item); } if(attrs.minNumbers){ var itemValidity = (viewValue && /\d/.test(viewValue)); scope.valid = !itemValidity ? false : scope.valid; var item = { 'msg' : $filter('translate')('MINNUMBERS', {PARAM: attrs.minNumbers + ''}), 'class' : itemValidity ? 'valid' : undefined }; scope.requirementSpec[nameStr].push(item); } if(attrs.validUrl){ // if(viewValue.indexOf('http') == -1){ // viewValue = 'http://' + viewValue; // ctrl.$setViewValue(viewValue); // } // else if(viewValue.indexOf('http') != 0){ // var httpIndex = viewValue.indexOf('http');w // viewValue = viewValue.substr(httpIndex); // ctrl.$setViewValue(viewValue); // } // var urlPattern = new RegExp("(http|https)://[\w-]+(\.[\w-]+)+([\w.,@?^=%&amp;:/~+#-]*[\ w@ ?^=%&amp;/~+#-])?"); var urlPattern = new RegExp(/^(https?):\/\/(((([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([az]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([az]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([az]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([az]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([az]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([az]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([az]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i); //(http|https):\/\/([a-zA-Z0-9]+\.)?[a-zA-Z0-9][a-zA-Z0-9-]+\.[a-zA-Z]{2,6}?(.+){0, 100}$/i) var itemValidity = !viewValue || viewValue.length == 0 || urlPattern.test(viewValue); scope.valid = !itemValidity ? false : scope.valid; // console.log(itemValidity); var item = { 'msg' : $filter('translate')('VALIDURL'), 'class' : itemValidity ? 'valid' : undefined }; scope.requirementSpec[nameStr].push(item); } } if(scope.valid) { ctrl.$setValidity(nameStr, true); elm.removeClass('ng-required-invalid').removeClass('validatorError').removeClass('ng-invalid').addClass('ng-valid'); return viewValue; } else { ctrl.$setValidity(nameStr, false); return undefined; } } if(!scope.requirementSpec){ scope.requirementSpec = {}; } if(Boolean(attrs.valueRequired) || Boolean(attrs.minLength)){ ctrl.$setValidity(attrs.name, false); // elm.removeClass('ng-valid').addClass('ng-invalid'); } ctrl.$parsers.unshift(function(viewValue) { return validator(viewValue); }); ctrl.$formatters.unshift(function(viewValue) { if(viewValue && viewValue != "" && viewValue.length > 0) return validator(viewValue); }); } }; }) 
+7
javascript angularjs google-chrome
source share
2 answers

The following code is added at the bottom of my custom validation directive:

  scope.$on('triggerValidator', function(e, val){ var viewValue = typeof $ === 'function' ? $('[name="' + attrs.name + '"]').val() : document.getElementsByName(attrs.name)[0].value; if(viewValue && viewValue.length > 0){ try{ ctrl.$setViewValue(viewValue); validator(viewValue); } catch(SUPPRESS){} } }); 

And the bottom of my controller:

  $timeout(function(){ $rootScope.$broadcast('triggerValidator'); }, 500) 

Please note that when using jQuery, a latency of 200 ms was sufficient, and when using my own JS selectors, I had to increase the latency to 500 ms.

A try-catch block exists to catch the strange parser exception that Angular throws when trying to set the view value containing char '@' works fine, though !.

It is also useful to track the official problem in the Angular repository: https://github.com/angular/angular.js/issues/1460

+2
source share

I suspect this is due to the fact that chrome fills the fields before angular even bootstraps

If the fields were set before angular bootstraps, it should work fine. I think the problem is different: Chorme sets the fields after angular loaded, but did not inform angular about the state change.

There are several approaches that could work, but none of them would be considered very elegant:

  • To download manually - just give a delay of 150-350 m.
  • To save the values โ€‹โ€‹yourself in local storage and set them manually. Here is a good module that supports it.
  • To use $ timeout about 2-10 times in 100-200 ms to explicitly force $digest .

I would probably go with option 3. , since it is easiest to implement and probably the most reliable.

+1
source share

All Articles