首页 文章

将焦点设置在AngularJs表单中的第一个无效输入上

提问于
浏览
58

我已经阅读了几篇与AngularJs焦点设置相关的文章和StackOverflow问题 .

不幸的是,我读过的所有例子都假设我可以添加一些属性来获得焦点,例如a focusMe directive .

但是,如果我事先不知道将焦点设置为哪个输入呢?特别是如何将焦点设置为具有$ invalid set的表单中的第一个输入元素 - 即一个未通过验证的元素 . 可能有几个输入未通过验证,因此我无法使用仅基于此尝试调用.focus()的指令 . (我这样做是出于辅助功能/ WCAG的原因,这样做的好方法就是单击提交时最小化按键以找到验证失败的第一个字段) .

$ error对象将为所有未通过验证的控件提供帮助,但它们按照表单上任何外观顺序的失败类型进行分组 .

我相信我能想出一些这样做的方法 . 表单上的指令,当需要设置焦点时接收一些广播 - 该指令然后可以搜索第一个$ invalid元素 . 然而,这似乎非常复杂,我想知道这些是否是更好的“角度”方式 .

12 回答

  • 0

    好的,所以答案比我想象的要简单 .

    我需要的只是一个指令来放置表单本身,事件处理程序查找提交事件 . 然后,这可以遍历DOM,查找其上具有.ng-invalid类的第一个元素 .

    使用jQLite的示例:

    myApp.directive('accessibleForm', function () {
        return {
            restrict: 'A',
            link: function (scope, elem) {
    
                // set up event handler on the form element
                elem.on('submit', function () {
    
                    // find the first invalid element
                    var firstInvalid = elem[0].querySelector('.ng-invalid');
    
                    // if we find one, set focus
                    if (firstInvalid) {
                        firstInvalid.focus();
                    }
                });
            }
        };
    });
    

    这里的示例使用Attribute指令,您可以展开示例以使其成为元素指令(restrict:'E')并包含将其转换为a的模板 . 然而,这是个人偏好 .

  • 7

    您可以创建指令作为其他一些答案,或者您可以将其与 ng-submit 挂钩并在控制器中实现逻辑 .

    视图:

    <form name='yourForm' novalidate ng-submit="save(yourForm)">
    </form>
    

    控制器:

    $scope.save = function(yourForm) {
      if (!yourForm.$valid) {
        angular.element("[name='" + yourForm.$name + "']").find('.ng-invalid:visible:first').focus();
        return false;
      }
    };
    
  • -1

    您也可以使用angular.element

    angular.element('input.ng-invalid').first().focus();
    

    View

    <form name="myForm" novalidate="novalidate" data-ng-submit="myAction(myForm.$valid)" autocomplete="off"></form>
    

    Controller

    $scope.myAction= function(isValid) {
        if (isValid) {
            //You can place your ajax call/http request here
        } else {
            angular.element('input.ng-invalid').first().focus();
        }
    };
    

    使用ngMessages进行验证

    The no jquery way

    angular.element($document[0].querySelector('input.ng-invalid')).focus();
    

    使用此方法时,需要在角度控制器中传递 $document 作为参数

    angular.module('myModule')
    .controller('myController', ['$document', '$scope', function($document, $scope){
        // Code Here
    }]);
    
  • 0

    您可以使用纯jQuery来选择第一个无效输入:

    $('input.ng-invalid').first().focus();

  • 12
    .directive('accessibleForm', function () {
            return {
                restrict: 'A',
                link: function (scope, elem) {
                    // set up event handler on the form element
                    elem.on('submit', function () {
                        // find the first invalid element
                        var firstInvalid = elem[0].querySelector('.ng-invalid');
                        if (firstInvalid && firstInvalid.tagName.toLowerCase() === 'ng-form') {
                            firstInvalid = firstInvalid.querySelector('.ng-invalid');
                        }
                        // if we find one, set focus
                        if (firstInvalid) {
                            firstInvalid.focus();
                        }
                    });
                }
            };
        })
    
  • 2

    我已经玩了一段时间这个想法,我想出了自己的解决方案,它可能会帮助那些不喜欢爬行DOM的人,就像我一样 .

    据我所知,表单元素以一致的顺序(即从上到下)注册自己,并且通过表单名称(例如$ scope.myForm),它们的名称和验证状态在范围内可用 .

    这让我认为有一种方法可以找到第一个无效的表单输入,而无需爬行DOM,而是爬行角度js的内部结构 . 下面是我的解决方案,但它假设您有一些其他方式来聚焦表单元素,我正在广播到自定义指令,如果广播匹配它将自己关注的元素的名称(当你到达时,这本身是有用的控制哪个元素关注第一个负载) .

    查找第一个无效的功能(理想情况下通过服务与控制器共享)

    function findFirstInvalid(form){
        for(var key in form){
            if(key.indexOf("$") !== 0){
                if(form[key].$invalid){
                    return key;
                }
            }
        }
    }
    

    和自定义焦点指令

    directives.directive('focus', function($timeout){
        return {
            require: 'ngModel',
            restrict: 'A',
            link: function(scope, elem, attrs, ctrl){
                scope.$on('inputFocus', function(e, name){
                    if(attrs.name === name){
                        elem.focus();
                    }
                });
            }
        }
    });
    
  • 15

    我对iandotkelly写的伟大解决方案进行了一些小修改 . 此解决方案添加了一个在滚动时触发的动画,并在此之后将焦点对准所选元素 .

    myApp.directive('accessibleForm', function () {
        return {
            restrict: 'A',
            link: function (scope, elem) {
    
                // set up event handler on the form element
                elem.on('submit', function () {
    
                    // find the first invalid element
                    var firstInvalid = elem[0].querySelector('.ng-invalid');
    
                    // if we find one, we scroll with animation and then we set focus
                    if (firstInvalid) {
                         angular.element('html:not(:animated),body:not(:animated)')
                        .animate({ scrollTop: angular.element(firstInvalid).parent().offset().top },
                            350,
                            'easeOutCubic',
                            function () {
                                firstInvalid.focus();
                            });
                    }
                });
            }
        };
    });
    
  • 1

    只有一行:

    if($scope.formName.$valid){
        //submit
    }
    else{
        $scope.formName.$error.required[0].$$element.focus();
    }
    
  • 0

    您可以在每个表单元素中添加一个属性,该元素是一个接收字段ID的函数(理想情况下是一个指令) . 此字段ID必须以某种方式与您的$ error对象相关联 . 该函数可以检查id是否在$ error对象中,如果是,则返回错误的属性设置 .

    <input id="name" class="{{errorCheck('name')}}">
    

    如果您有错误,它会生成此错误 .

    <input id="name" class="error">
    

    您可以使用它来设置样式,现在您知道哪些字段有错误 . 不幸的是你不知道哪个是第一个字段 .

    一种解决方案是使用jQuery和.first过滤器 . 如果你走这条路,请查看http://docs.angularjs.org/api/angular.element

    另一种解决方案是在表单字段中添加函数的字段顺序参数:{{errorCheck('name',1)}} . 您可以将错误字段名称推送到数组,然后按字段顺序参数对它们进行排序 . 这可以为您提供更大的灵活性 .

    希望这个帮助 .

  • 1

    我受到上面的chaojidan的启发,为那些使用嵌套角1.5.9 ng形式的人建议这种变化:

    class FormFocusOnErr implements ng.IDirective
    {
        static directiveId: string = 'formFocusOnErr';
    
        restrict: string = "A";
    
        link = (scope: ng.IScope, elem, attrs) =>
        {
            // set up event handler on the form element
            elem.on('submit', function () {
    
                // find the first invalid element
                var firstInvalid = angular.element(
                    elem[0].querySelector('.ng-invalid'))[0];
    
                // if we find one, set focus
                if (firstInvalid) {
                    firstInvalid.focus();
                    // ng-invalid appears on ng-forms as well as 
                    // the inputs that are responsible for the errors.
                    // In such cases, the focus will probably fail 
                    // because we usually put the ng-focus attribute on divs 
                    // and divs don't support the focus method
                    if (firstInvalid.tagName.toLowerCase() === 'ng-form' 
                        || firstInvalid.hasAttribute('ng-form') 
                        || firstInvalid.hasAttribute('data-ng-form')) {
                        // Let's try to put a finer point on it by selecting 
                        // the first visible input, select or textarea 
                        // that has the ng-invalid CSS class
                        var firstVisibleInvalidFormInput = angular.element(firstInvalid.querySelector("input.ng-invalid,select.ng-invalid,textarea.ng-invalid")).filter(":visible")[0];
                        if (firstVisibleInvalidFormInput) {
                            firstVisibleInvalidFormInput.focus();
                        }
                    }
                }
            });            
        }
    }
    
    // Register in angular app
    app.directive(FormFocusOnErr.directiveId, () => new FormFocusOnErr());
    
  • 4

    那是因为jqLite和元素上的Angular文档不支持 focus() .

  • 87

    基于非指令的方法可能如下所示 . 这是我使用的,因为我在页面底部有一个“下一个”按钮实际上在页脚的index.html中 . 我在main.js中使用此代码 .

    if (!$scope.yourformname.$valid) {
          // find the invalid elements
          var visibleInvalids = angular.element.find('.ng-invalid:visible');
    
    
          if (angular.isDefined(visibleInvalids)){
            // if we find one, set focus
            visibleInvalids[0].focus();
          }
    
          return;
        }
    

相关问题