将焦点设置为以AngularJsforms的第一个无效input

我已经阅读了几篇关于在AngularJs中设置焦点的文章和StackOverflow问题。

不幸的是,我所读到的所有例子都假定我可以添加一些属性来获得焦点,例如focusMe指令 。

但是,如果我不知道哪些input设置重点? 特别是,如何将焦点设置为具有$ invalid set的表单中的第一个input元素 – 即validation失败的元素。 可能有几个input未通过validation,所以我不能使用一个只是试图调用基于此的.focus()的指令。 (我这样做是为了Accessibility / WCAG的原因,它的好做法是通过提交被点击来最小化按键来findvalidation失败的第一个字段)。

$ error对象将给所有失败validation的控件,但是它们按照失败的types进行分组,而不是以任何forms出现在窗体上。

我敢肯定,我可以想出这样做的一些克服的方式。 在表单上有一个指令,在需要设置焦点时接收一些广播 – 然后该指令可以search第一个$ invalid元素。 然而,这似乎是非常复杂的,我想知道这是否是一个更好的“angular”方式做到这一点。

好吧,答案比我想象的要简单。

所有我需要的是一个指令,把自己的forms,与一个事件处理程序寻找提交事件。 这可以遍历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'),并且包含一个将它转换成一个模板的模板。 不过这是个人偏好。

您可以创build指令,作为一些其他的答案,或者你可以用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; } }; 

你也可以使用angular.element

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

视图

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

调节器

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

使用ngMessages进行validation

没有jQuery的方式

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

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

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

您可以使用纯jQuery来select第一个无效的input:

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

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

我一直在玩这个想法一段时间,我提出了自己的解决scheme,它可能会帮助像我一样不利于爬行DOM的人。

据我可以告诉表单元素以一致的顺序(即从上到下)注册自己,他们的名字和validation状态通过任何forms名称(例如$ scope.myForm)在范围上可用。

这导致我认为有没有办法find第一个无效的表单input,而不爬行的DOM,而是爬行angularjs的内部结构。 下面是我的解决scheme,但它假设你有其他的方式来聚焦表单元素,我广播到一个自定义指令,如果广播匹配的元素的名称,它将本身的焦点(这是有用的本身,你会得到控制哪个元素关注第一个负载)。

find第一个无效的函数(最好通过服务共享给控制器)

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

我受到上面的chaojidan的启发,为那些使用嵌套angular1.5.9 ngforms的人提出这个变化:

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

我对iandotkelly编写的伟大解决scheme做了一些小修改。 这个解决scheme添加了一个滚动触发的animation,然后在选定的元素上执行一个焦点。

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

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

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

如果你有一个错误,它会产生这个。

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

你可以使用它来设置你的风格,你现在知道哪些字段有错误。 不幸的是,你不知道哪个是第一个领域。

一个解决scheme是使用jQuery和.firstfilter。 如果你走这条路,看看http://docs.angularjs.org/api/angular.element

另一个解决scheme是添加到你的表单字段的函数的字段顺序参数:{{errorCheck('name',1)}}。 您可以将错误字段名称推送到数组,然后按字段顺序参数对它们进行sorting。 这可以给你更多的灵活性。

希望这可以帮助。

这是因为focus()不支持jqLit​​e和Angular文档中的元素。

只有一行:

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

基于非指令的方法可能如下所示。 这是我用的,因为我有一个“下一步”button在每一页的底部实际上是在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; }