Set focus on first invalid input in AngularJs form

前端 未结 13 1411
-上瘾入骨i
-上瘾入骨i 2020-12-04 18:55

I\'ve read several articles and StackOverflow questions relating to the setting of focus in AngularJs.

Unfortunately all the examples that I have read assume that th

相关标签:
13条回答
  • 2020-12-04 19:26

    That's because focus() is not supported in jqLite and from the Angular docs on element.

    0 讨论(0)
  • 2020-12-04 19:29

    You can use pure jQuery to select the first invalid input:

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

    0 讨论(0)
  • 2020-12-04 19:29

    I did some small modifications to the great solution written by iandotkelly. This solution adds an animation that is triggered on scroll, and does a focus to the selected element after that.

    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();
                            });
                    }
                });
            }
        };
    });
    
    0 讨论(0)
  • 2020-12-04 19:32

    I was inspired by chaojidan above to suggest this variation for those who are using nested angular 1.5.9 ng-forms:

    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());
    
    0 讨论(0)
  • 2020-12-04 19:34

    I have been playing with this idea for a while and I came up with my own solution, it may help people who are adverse to crawling the DOM, like me.

    As far as I can tell form elements register themselves in a consistent order (i.e. top to bottom) and their names and validation states are available on the scope through what ever the form name is (e.g. $scope.myForm).

    This lead me to think that there was a way to find the first invalid form input without crawling the DOM and instead crawling the internal structures of angular js. Below is my solution but it assumes that you have some other way of focusing form elements, I am broadcasting to a custom directive, if the broadcast matches the name of the element it will focus itself (which is useful in itself as you you get to control which element takes focus on the first load).

    The function to find the first invalid (ideally shared to the controllers through a service)

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

    And the custom focus directive

    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();
                    }
                });
            }
        }
    });
    
    0 讨论(0)
  • 2020-12-04 19:34

    You can add an attribute in each form element which is a function (ideally a directive) that receives a field id. This field id would have to correlate somehow to your $error object. The function can check if the id is in your $error object, and if so return the attribute setting for an error.

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

    If you had an error, it would generate this.

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

    You can use this to set your style and you now know which fields have errors. Unfortunately you don't know which is the first field.

    One solution would be to use jQuery and the .first filter. If you go this route, check out http://docs.angularjs.org/api/angular.element

    Another solution would be to add into your form fields a field order parameter for the function: {{errorCheck('name', 1)}}. You could push the error field names to an array, then sort them by the field order parameter. This could give you more flexibility.

    Hope this helps.

    0 讨论(0)
提交回复
热议问题