Установите фокус на первый недопустимый ввод в форме AngularJs

Я прочитал несколько статей и вопросы StackOverflow, касающиеся установки фокуса в AngularJs.

К сожалению, все примеры, которые я прочитал, предполагают, что есть некоторый атрибут, который я могу добавить к элементу, чтобы получить фокус, например. a директива focusMe.

Однако, что, если я не знаю заранее, какой вход для установки фокуса? В частности, как настроить фокус на первый элемент ввода в форме с $недопустимым множеством, то есть элементом, который не прошел проверку. Может быть несколько входов, которые не подтверждают проверку, поэтому я не могу использовать директиву, которая просто пытается вызвать .focus() на основе этого. (Я делаю это по причинам доступности /WCAG, его хорошая практика - сделать это нажатием кнопки, чтобы свести к минимуму нажатия клавиш, чтобы найти первое поле с неудачной проверкой).

Объект $error предоставит все элементы управления, которые не подтверждают проверку, но они сгруппированы по типу отказа не в любом порядке появления в форме.

Я уверен, что смогу придумать какой-то неуклюжий способ сделать это. Директива по форме, которая получает некоторую трансляцию, когда нужно настроить фокус, - это директива может затем искать первый недопустимый элемент. Однако это кажется очень сложным, и я хотел бы знать, является ли это более удобным способом angular.

Ответ 1

Итак, ответ был проще, чем я думал.

Все, что мне было нужно, - это директива по размещению самой формы, с обработчиком событий, который ищет событие отправки. Затем это может пересечь 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, вы можете расширить этот пример, чтобы это было директивой элемента (ограничивайте: "E" ) и включите шаблон, который преобразует его в a. Это, однако, личное предпочтение.

Ответ 2

Вы можете создать директиву как некоторые другие ответы или, альтернативно, вы можете подключить ее с помощью 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;
  }
};

Ответ 3

Вы также можете использовать 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 для проверки

Способ без jquery

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

При использовании этого метода необходимо передать $document в качестве параметра в вашем контроллере angular

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

Ответ 4

Вы можете использовать чистый jQuery для выбора первого недопустимого ввода:

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

Ответ 5

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

Ответ 6

Я играл с этой идеей некоторое время, и я придумал свое собственное решение, это может помочь людям, которые отрицательно сканируют DOM, как я.

Насколько я могу сказать, элементы формы регистрируются в последовательном порядке (т.е. сверху вниз), а их имена и состояния проверки доступны в области видимости через то, что когда-либо было в имени формы (например, $scope.myForm).

Это заставило меня подумать, что существует способ найти первый недопустимый ввод формы без обхода DOM и вместо этого сканировать внутренние структуры angular 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();
                }
            });
        }
    }
});

Ответ 7

Я сделал некоторые небольшие изменения в отличном решении, написанном 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();
                        });
                }
            });
        }
    };
});

Ответ 8

только одна строка:

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

Ответ 9

Вы можете добавить атрибут в каждый элемент формы, который является функцией (в идеале директивой), которая получает идентификатор поля. Этот идентификатор поля должен был бы как-то соотнести с вашим объектом $error. Функция может проверить, находится ли идентификатор в объекте $error, и если это так, возвращаем параметр атрибута для ошибки.

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

Если у вас была ошибка, это создало бы это.

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

Вы можете использовать это, чтобы задать свой стиль, и теперь вы знаете, какие поля имеют ошибки. К сожалению, вы не знаете, что является первым полем.

Одним из решений было бы использовать jQuery и фильтр .first. Если вы идете по этому маршруту, http://docs.angularjs.org/api/angular.element

Другим решением было бы добавить в ваши поля формы параметр порядка поля для функции: {{errorCheck ('name', 1)}}. Вы можете указать имена полей ошибок в массив, а затем отсортировать их по параметру порядка поля. Это может дать вам большую гибкость.

Надеюсь, что это поможет.

Ответ 10

Я был вдохновлен chaojidan выше, чтобы предложить этот вариант для тех, кто использует вложенные 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 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());

Ответ 11

Это потому, что focus() не поддерживается в jqLite и из Angular docs на элементе.

Ответ 12

Небольшая настройка того, что сказал @Sajan, сработала для меня,

angular.element("[name='" + this.formName.$name + "']").find('.ng-invalid:visible:first')[0].focus();

Ответ 13

Метод, не основанный на указаниях, может выглядеть так. Это то, что я использовал, так как у меня есть "следующая" кнопка внизу каждой страницы, которая на самом деле находится в 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;
    }