【问题标题】:$parsers \ $formatters functions are not firing when the browser fills the form field automatically当浏览器自动填充表单字段时,$parsers \ $formatters 函数不会触发
【发布时间】:2013-12-27 19:50:42
【问题描述】:

我的应用程序中有一个特定的自定义验证指令(代码附在下面)。 问题在于,当需要一个或多个表单字段并且 chrome 会自动填充它们时,这些字段将保持无效,直到用户手动更改它们。 我怀疑这是因为 chrome 在 Angular 甚至引导之前填充了字段。

有没有办法解决这个问题。

代码:

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 && /[A-z]/.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?):\/\/(((([a-z]|\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]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\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);
            });
        }
    };
})

【问题讨论】:

    标签: javascript google-chrome angularjs


    【解决方案1】:

    我怀疑这是因为 chrome 在 Angular 甚至引导之前填充了字段

    如果这些字段是在 Angular 引导之前设置的——它应该可以正常工作。我认为问题是反过来的:Chorme 在 Angular 启动后设置字段,但它没有通知 Angular 状态更改。

    有几种方法可以奏效,但没有一种会被认为是非常优雅的:

    1. 手动引导 - 只需延迟 150-350 毫秒即可。
    2. 将值自行保存在本地存储中并手动设置。 Here 是支持它的好模块。
    3. 以 100-200 毫秒为间隔使用 $timeout 大约 2-10 次来显式强制 $digest

    我可能会选择3. 选项,因为它最容易实现并且可能最可靠。

    【讨论】:

    • 感谢您的回复,我会检查一下,如果您的解决方案有效,我会通知您!
    【解决方案2】:

    在我的自定义验证指令底部添加了以下代码:

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

    我的控制器底部有以下内容:

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

    请注意,当使用 jQuery 时,200ms 的超时时间就足够了,而当使用原生 JS 选择器时,我不得不将超时时间增加到 500ms。

    try-catch 块用于捕获 Angular 在尝试设置包含字符“@”的视图值时抛出的奇怪解析器异常,但工作正常!

    在 Angular 存储库中跟踪官方问题也很好: https://github.com/angular/angular.js/issues/1460

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-17
      • 2011-06-23
      • 2017-04-09
      • 2021-08-30
      • 2017-08-22
      • 1970-01-01
      • 2012-02-01
      • 1970-01-01
      相关资源
      最近更新 更多