Вопрос по wcag, angularjs, angularjs-directive, validation – Установить фокус на первый неверный ввод в форме AngularJs

59

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

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

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

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

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

Ваш Ответ

12   ответов
1

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

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

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

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

@pcatre - я также вижу, что вы произвели редактирование, которое было отклонено, предлагая изменить на 'A' директива атрибута. Конечно, директива A или даже директива C тоже подойдет, но почемуT директива E работает для вас? Кажется, это работает просто отлично для меня. iandotkelly
Это на самом деле не так плохо, как производительностьпересекая дом " может звучать. QuerySelector является родным методом во всех современных браузерах, имеет хорошую оптимизацию и очень быстр. Гораздо быстрее, чем обходить какую-то коллекцию объектов JavaScript. awe
elem[0] возвращает вам элемент документа, а затемquerySelector возвращает уже только первый матч. в чем причина, чтобы обернуть это вangular.element а затем развернуть его снова с?[0] danza
elem ужеangular.element()так что если выВы используете JQuery по крайней мере, вы можете просто сделатьelem.find('.ng-invalid:first').focus(), Не уверен, что jqLite поддерживает:first селектор тоже. roryf
15

Вы можете создать директиву, как некоторые другие ответы, или вы можете подключить ееng-submit и реализовать логику в контроллере.

Посмотреть:



контроллер:

$scope.save = function(yourForm) {
  if (!yourForm.$valid) {
    angular.element("[name='" + yourForm.$name + "']").find('.ng-invalid:visible:first').focus();
    return false;
  }
};
angular.element($document[0].querySelector('input.ng-invalid'))[0].focus(); работал на меня Manwal
не сJQlite.. tryingHard
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();
                }
            });
        }
    }
});
Эй, я обязательно подробно рассмотрю это и попробую. Обход DOM кажется таким неэффективным. Если это работает для меня, я переключу принять к этому! iandotkelly
0

Я был вдохновленchaojidan выше, чтобы предложить этот вариант для тех, кто использует вложенные угловые формы 1.5.9 нг:

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

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


Если бы у вас была ошибка, это сгенерирует это.


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

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

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

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

Привет спасибо У меня есть способ добавить атрибут к элементам формы, которые потерпели неудачу - яЯ просто ищу наиболее угловой путь и пересекаю DOM, ища этот атрибут, кажется глупым, но, вероятно, единственным способом. iandotkelly
4

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

Привет. Было бы полезно, если бы вы объяснили, почему это лучший ответ. Я'Буду рад сделать это принятым ответом, если у него есть какое-то преимущество. iandotkelly
12

Вы также можете использовать angular.element

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

Посмотреть


контроллер

$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.module('myModule')
.controller('myController', ['$document', '$scope', function($document, $scope){
    // Code Here
}]);
angular.element($document[0].querySelector('input.ng-invalid'))[0].focus(); работал на меня. Manwal
@iandotkelly Я также добавил не jquery метод .. надеюсь, это поможет Sajan Mullappally
-1

Ненаправленный метод может выглядеть следующим образом. Это то, что я использовал, так как у меня естьследующий' Кнопка внизу каждой страницы, которая на самом деле находится в 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;
    }
используя угловойВстроенный JQLite этот код не будет работать. Сначала обязательно позвонитеangular.element().findнеangular.element.find чтобы избежать исключения. Во-вторых, это только когда-нибудь вернет пустой массив из-за angular-jqLite 'ограничения. Pat Fowler
Просто и понятно, как мне нравится cen
7

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

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

как добавить анимацию к этому при прокрутке? Sateesh Kumar Alli
И куда пойдет этот код? А что если бы мы нене хотите, чтобы JQuery был частью решения? Суть вопроса не в том, как мне сфокусировать элемент (что можно сделать с небольшим количеством JavaScript, даже без jQuery) - вопрос в том, как мне это сделать, чтобы он был сфокусирован, когда на кнопке submit нажимается неверная форма. iandotkelly
решить мою проблему с фокусировкой на угловой форме! mmw5610
1

Я сделал несколько небольших модификаций для отличного решения, написанного 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();
                        });
                }
            });
        }
    };
});
Было бы очень полезно, если вы подключите plnkr или jsfiddle Ammar Hayder Khan
0

Тот'потому чтоfocus() не поддерживается в jqLite и в документах Angular по элементам.

Это комментарий к CarComp 'ответ s - не ответ сам по себе iandotkelly

Похожие вопросы