Установить фокус на первый недопустимых входных данных на AngularJS форма
Я прочитал несколько статей и вопросов StackOverflow, связанных с настройкой фокуса в AngularJs.
к сожалению, все примеры, которые я прочитал, предполагают, что есть некоторый атрибут, который я могу добавить к элементу, чтобы получить фокус, например директиву focusMe.
однако что делать, если я заранее не знаю, на какой вход установить фокус? В частности, как установить фокус на первый элемент ввода в форме ,которая имеет $ invalid set-т. е. элемент, который терпит неудачу утверждение. Там может быть несколько входов, которые не прошли проверку, поэтому я не могу использовать директиву, которая просто пытается вызвать .фокус() на основе этого. (Я делаю это по причинам доступности/WCAG, его хорошая практика, чтобы сделать это при отправке щелчка, чтобы свести к минимуму нажатия клавиш, чтобы найти первое поле, которое не прошло проверку).
объект $error предоставит все элементы управления, которые не прошли проверку, но они сгруппированы по типу сбоя не в любом порядке появления на экране форма.
Я уверен, что могу придумать какой-то клуджированный способ сделать это. Директива на форме, которая получает некоторую широковещательную передачу, когда фокус должен быть установлен - эта директива может затем искать первый элемент $invalid. Однако это кажется очень сложным, и я хотел бы знать, являются ли они лучшим более "угловым" способом сделать это.
12 ответов:
хорошо, так что ответ был проще, чем я думал.
все, что мне было нужно, это директива, чтобы поставить на самой форме, с обработчиком событий ищет событие submit. Это может затем пересечь DOM ищет первый элемент, который имеет. ng-недопустимый класс на нем.
пример использования 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(); } }); } }; });в Примере здесь используется директива атрибута, вы можете развернуть пример, чтобы это была директива элемента (restrict: 'E') и включить шаблон который преобразует это . Однако это личное предпочтение.
вы можете создать директиву в качестве некоторых других ответов или же вы можете подключить его с
ng-submitи реализовать логику в контроллер.View:
:<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('input.ng-invalid').first().focus();View
<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.module('myModule') .controller('myController', ['$document', '$scope', function($document, $scope){ // Code Here }]);
вы можете использовать чистый jQuery для выбора первого недопустимого ввода:
$('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(); } }); } }; })
Я играл с этой идеей некоторое время, и я придумал свое собственное решение, это может помочь людям, которые не хотят ползать по DOM, как и я.
насколько я могу сказать, элементы формы регистрируются в согласованном порядке (т. е. сверху вниз), и их имена и состояния проверки доступны в области действия через то, что когда-либо имя формы (например, $scope.ссылки).
это привело меня к мысли, что был способ найти первый недопустимый ввод формы без приполз в дом и вместо ползания внутренних структур угловой JS. Ниже приведено мое решение, но оно предполагает, что у вас есть какой-то другой способ фокусировки элементов формы, я транслирую пользовательскую директиву, если трансляция соответствует имени элемента, он будет фокусироваться сам (что полезно само по себе, поскольку вы можете контролировать, какой элемент фокусируется на первой загрузке).
функция для поиска первого недопустимого (в идеале совместно с контроллерами через a сервис)
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(); } }); } } });
Я сделал некоторые небольшие изменения в большое решение, написанное 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(); }); } }); } }; });
всего одна строчка:
if($scope.formName.$valid){ //submit } else{ $scope.formName.$error.required[0].$$element.focus(); }
вы можете добавить атрибут в каждый элемент формы, который является функцией (в идеале директивой), которая получает идентификатор поля. Этот идентификатор поля должен каким-то образом коррелировать с вашим объектом $error. Функция может проверить, находится ли идентификатор в вашем объекте $error, и если да, то возвращает параметр атрибута для ошибки.
<input id="name" class="{{errorCheck('name')}}">Если у вас была ошибка, она приведет этого.
<input id="name" class="error">Вы можете использовать это, чтобы установить свой стиль и теперь вы знаете, какие поля содержат ошибки. К сожалению, вы не знаете, что является первым полем.
одним из решений было бы использовать jQuery и .первый фильтр. Если вы идете по этому маршруту, проверьте http://docs.angularjs.org/api/angular.element
надеюсь, что это помогает.
Я был вдохновлен 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());
директивный метод может выглядеть следующим образом. Это то, что я использовал, так как у меня есть кнопка "Далее" в нижней части каждой страницы, которая на самом деле находится в индексе.html в нижнем колонтитуле. Я использую этот код в главном.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; }
Comments