Проверять поля после того, как пользователь покинул поле


92

С помощью AngularJS я могу использовать ng-pristineили, ng-dirtyчтобы определить, ввел ли пользователь это поле. Однако я хочу выполнять проверку на стороне клиента только после того, как пользователь покинул область поля. Это связано с тем, что, когда пользователь вводит такое поле, как электронная почта или телефон, он всегда будет получать сообщение об ошибке до тех пор, пока он не завершит ввод своего электронного письма полностью, и это не является оптимальным для пользователя.

пример


ОБНОВИТЬ:

Angular теперь поставляется с настраиваемым событием размытия: https://docs.angularjs.org/api/ng/directive/ngBlur


3
Я предполагаю, что это то, что angularjs должен был поддерживать ...
csg 05

Может быть. Встроенная проверка формы работает достаточно хорошо, и я не знаю, хочу ли я, чтобы она была встроена. В конце концов, в большинстве случаев я хочу, чтобы Angular проверял немедленно. то есть в числовом поле я хочу, чтобы форма немедленно становилась недействительной, если пользователь начинает вводить буквы.
mcranston18 06

11
проверка размытия распространена с 15 лет ...
Olivvv

Ответы:


75

Angular 1.3 теперь имеет ng-model-options, и вы можете установить эту опцию, { 'updateOn': 'blur'}например, и вы даже можете отклонить, когда используется либо слишком быстрый ввод, либо вы хотите сэкономить несколько дорогостоящих операций DOM (например, написание модели в несколько мест DOM, и вы не хотите, чтобы цикл $ digest происходил при каждом нажатии клавиши)

https://docs.angularjs.org/guide/forms#custom-triggers и https://docs.angularjs.org/guide/forms#non-immediate-debounced-model-updates.

По умолчанию любое изменение содержимого вызывает обновление модели и проверку формы. Вы можете переопределить это поведение, используя директиву ngModelOptions для привязки только к указанному списку событий. Т.е. ng-model-options = "{updateOn: 'blur'}" будет обновляться и проверяться только после того, как элемент управления потеряет фокус. Вы можете установить несколько событий, используя список, разделенный пробелами. Т.е. ng-model-options = "{updateOn: 'mousedown blur'}"

И отрицают

Вы можете отложить обновление / проверку модели, используя ключ debounce с директивой ngModelOptions. Эта задержка также будет применяться к анализаторам, валидаторам и флагам модели, таким как $ dirty или $ pristine.

Т.е. ng-model-options = "{debounce: 500}" будет ждать полсекунды с момента последнего изменения содержимого, прежде чем запускать обновление модели и проверку формы.


2
В большинстве случаев это не решает проблему, потому что вы, вероятно, все еще хотите получать ng-changeсобытия до того, как пользователь покинет поле. Например, в моем случае я запрашиваю новые данные из своего API, как только поле ввода становится действительным, но я не хочу показывать сообщение об ошибке, если оно недействительно - я хочу сделать это только тогда, когда оно недопустим, и произошло событие размытия.
Tom

@Tom Я тестирую, 1.3.0 rc-3и он не срабатывает, changeпока не сработаетblur , проверьте это: plnkr.co/edit/RivVU3r7xfHO1hUybqMu?p=preview
Джилл Бейтс

Это действительно должен быть принятый главный ответ. Зачем добавлять директиву, если можно добавить одно свойство?
Дэн Халтон

92

Начиная с версии 1.3.0 это можно легко сделать $touched, что верно после того, как пользователь покинул поле.

<p ng-show="form.field.$touched && form.field.$invalid">Error</p>

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController


1
Разве это не будет выполнять проверку после каждого включения ключа? OP заявил, что он хотел провести проверку только после того, как пользователь покинул поле.
comecme

@comecme, да, по умолчанию ng-model-options, updateOn: 'default'что означает, что для входов, ключа вверх и для выбора, при изменении
pocesar

5
Однако большой недостаток здесь в том, что у вас не будет такого же опыта валидации, если вы вернетесь в поле. Вы вернулись на круги своя, где он проверяется при каждом нажатии клавиши, поскольку он уже настроен на прикосновение. Я удивлен, что за него так много голосов.
dudewad

3
@dudewad, это правильно, но я думаю, что это желаемое поведение с точки зрения UX - если вы вернетесь к недопустимому полю, сообщение об ошибке должно сохраняться до тех пор, пока значение не станет действительным, а не до тех пор, пока вы не введете первую букву.
danbars

@dudewad Разве это не поможет пользователю проверять каждое нажатие после того, как они уже знают, что поле недействительно после первого заполнения? Как только они узнают, что поле недействительно, вероятно, они захотят быстро узнать, когда поле действительно, и, возможно, проверка при каждом нажатии клавиши ускорит этот процесс?
Алан Даннинг,

32

Я решил это, расширив то, что предложил @jlmcdonald. Я создал директиву, которая будет автоматически применяться ко всем элементам ввода и выбора:

var blurFocusDirective = function () {
    return {
        restrict: 'E',
        require: '?ngModel',
        link: function (scope, elm, attr, ctrl) {
            if (!ctrl) {
                return;
            }

            elm.on('focus', function () {
                elm.addClass('has-focus');

                scope.$apply(function () {
                    ctrl.hasFocus = true;
                });
            });

            elm.on('blur', function () {
                elm.removeClass('has-focus');
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

            elm.closest('form').on('submit', function () {
                elm.addClass('has-visited');

                scope.$apply(function () {
                    ctrl.hasFocus = false;
                    ctrl.hasVisited = true;
                });
            });

        }
    };
};

app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

Это добавит has-focusи has-visitedклассы к различным элементам , как пользователь фокусирует / визиты элементов. Затем вы можете добавить эти классы в свои правила CSS, чтобы отображать ошибки проверки:

input.has-visited.ng-invalid:not(.has-focus) {
    background-color: #ffeeee;   
}

Это хорошо работает, поскольку элементы по-прежнему получают недействительные свойства и т. Д., Но CSS можно использовать, чтобы улучшить взаимодействие с пользователем.


2
Почему требуется '? NgModel' вместо простого 'ngModel'? Я понимаю, что там должна быть ngModel, но почему знак вопроса?
Noremac

2
Спасибо за идею. Следует отметить, что для этого требуется jQuery (я думаю - не видел .closest () в jql).
iandotkelly

1
Не все элементы <input> поддерживаются ngModel (например, input type = submit). ? требует ngModel только для элементов, которые его поддерживают.
chmanie

5
Честно говоря, вы не должны отклоняться от углового способа установки состояний поля ввода, например formName.inputName.$dirty. Я бы предложил отредактировать директиву, чтобы написать подобное логическое значение formName.inputName.$focused, вместо того, чтобы использовать имена классов для размытия и логические значения для проверки.
Tom

17

Мне удалось сделать это с помощью довольно простого CSS. Это действительно требует, чтобы сообщения об ошибках были братьями и сестрами входных данных, к которым они относятся, и чтобы они имели классerror .

:focus ~ .error {
    display:none;
}

После выполнения этих двух требований это скроет любое сообщение об ошибке, относящееся к сфокусированному полю ввода, что, я думаю, angularjs все равно должен делать. Похоже на недосмотр.


2
Ах. Это разумный способ. Я предпочитаю писать для этого javascript.
колода

5
Обратной стороной этого подхода является то, что когда кто-то исправляет ошибку, ошибка больше не видна, что не идеально. Идеально было бы, чтобы ошибка не отображалась до размытия, но не исчезала, пока ее не исправили.
Крис Никола

4

Кажется, это стандартно реализовано в новых версиях angular.

Классы ng-untouched и ng-touched устанавливаются соответственно до и после того, как пользователь сфокусировался на проверенном элементе.

CSS

input.ng-touched.ng-invalid {
   border-color: red;
}

4

Что касается решения @ lambinator ... Я получал следующую ошибку в angular.js 1.2.4:

Ошибка: [$ rootScope: inprog] $ дайджест уже выполняется

Я не уверен, что я сделал что-то не так или это изменение в Angular, но удаление scope.$apply операторов решило проблему, и классы / состояния все еще обновляются.

Если вы также видите эту ошибку, попробуйте следующее:

var blurFocusDirective = function () {
  return {
    restrict: 'E',
    require: '?ngModel',
    link: function (scope, elm, attr, ctrl) {
      if (!ctrl) {
        return;
      }
      elm.on('focus', function () {
        elm.addClass('has-focus');
        ctrl.$hasFocus = true;
      });

      elm.on('blur', function () {
        elm.removeClass('has-focus');
        elm.addClass('has-visited');
        ctrl.$hasFocus = false;
        ctrl.$hasVisited = true;
      });

      elm.closest('form').on('submit', function () {
        elm.addClass('has-visited');

        scope.$apply(function () {
          ctrl.hasFocus = false;
          ctrl.hasVisited = true;
        });
      });
    }
  };
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);

2

Возможно, вам удастся написать специальную директиву, которая обертывает метод javascript blur () (и запускает функцию проверки при срабатывании); есть проблема Angular, которая имеет образец (а также общую директиву, которая может связываться с другими событиями, которые изначально не поддерживаются Angular):

https://github.com/angular/angular.js/issues/1277

Если вы не хотите идти по этому пути, другим вариантом будет установка $ watch в поле, снова запускающая проверку при заполнении поля.


2

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

(Для примера требуется jQuery)

JavaScript

module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
    return {
        restrict: 'AC',
        link: function (scope, element, attrs) {
            $inputs = element.find('input, select, textarea');

            $inputs.on('blur', function () {
                $(this).addClass('has-visited');
            });

            element.on('submit', function () {
                $inputs.addClass('has-visited');
            });
        }
    };
});

CSS

input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
  color: #b94a48;
  border-color: #ee5f5b;
}

HTML

<form late-validate-form name="userForm">
  <input type="email" name="email" required />
</form>

2

на основе ответа @nicolas .. Чистый CSS должен помочь, он покажет только сообщение об ошибке при размытии

<input type="email" id="input-email" required
               placeholder="Email address" class="form-control" name="email"
               ng-model="userData.email">
        <p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>

CSS

.ng-invalid:focus ~ .bg-danger {
     display:none;
}

Это хорошо, но сообщение об ошибке исчезнет, ​​когда пользователь снова щелкнет по полю, что не идеально.
Bennett McElwee

2

Вот пример использования ng-messages (доступно в angular 1.3) и настраиваемой директивы.

Сообщение проверки отображается при размытии впервые, когда пользователь покидает поле ввода, но когда он исправляет значение, сообщение проверки немедленно удаляется (больше не размытие).

JavaScript

myApp.directive("validateOnBlur", [function() {
    var ddo = {
        restrict: "A",
        require: "ngModel",
        scope: {},
        link: function(scope, element, attrs, modelCtrl) {
            element.on('blur', function () {
                modelCtrl.$showValidationMessage = modelCtrl.$dirty;
                scope.$apply();
            });
        }
    };
    return ddo;
}]);

HTML

<form name="person">
    <input type="text" ng-model="item.firstName" name="firstName" 
        ng-minlength="3" ng-maxlength="20" validate-on-blur required />
    <div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
        <span ng-message="required">name is required</span>
        <span ng-message="minlength">name is too short</span>
        <span ng-message="maxlength">name is too long</span>
    </div>
</form>

PS. Не забудьте загрузить и включить ngMessages в свой модуль:

var myApp = angular.module('myApp', ['ngMessages']);

1

Параметры ng-model в AngularJS 1.3 (бета-версия на момент написания) задокументированы для поддержки {updateOn: 'blur'}. Для более ранних версий у меня работало что-то вроде следующего:

myApp.directive('myForm', function() {
  return {
    require: 'form',
    link: function(scope, element, attrs, formController) {
      scope.validate = function(name) {
        formController[name].isInvalid
            = formController[name].$invalid;
      };
    }
  };
});

С таким шаблоном:

<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>

1

Использовать состояние поля $ touched Для этого было изменено поле, как показано в примере ниже.

<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
    You must enter a value
</div>

0

Вы можете динамически установить класс css has-error (при условии, что вы используете bootstrap), используя ng-class и свойство в области связанного контроллера:

plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info

HTML:

<div ng-class="{'has-error': badEmailAddress}">
    <input type="email" class="form-control" id="email" name="email"
        ng-model="email" 
        ng-blur="emailBlurred(email.$valid)">
</div>

Контроллер:

$scope.badEmailAddress = false;

$scope.emailBlurred = function (isValid) {
    $scope.badEmailAddress = !isValid;
};

0

Если вы используете bootstrap 3 и lesscss, вы можете включить проверку размытия с помощью следующего фрагмента less:

:focus ~ .form-control-feedback.glyphicon-ok {
  display:none;
}

:focus ~ .form-control-feedback.glyphicon-remove {
  display:none;
}

.has-feedback > :focus {
  & {
    .form-control-focus();
  }
}

0

outЯ использовал директиву. Вот код:

app.directive('onBlurVal', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs, controller) {

            element.on('focus', function () {
                element.next().removeClass('has-visited');
                element.next().addClass('has-focus');
            });

            element.on('blur', function () {

                element.next().removeClass('has-focus');
                element.next().addClass('has-visited');
            });
        }
    }
})

Все мои элементы управления вводом содержат элемент span в качестве следующего элемента, в котором отображается мое сообщение проверки, и поэтому директива в качестве атрибута добавляется к каждому элементу управления вводом.

У меня также есть (необязательно) класс css .has-focus и has-visit в моем файле css, на который вы видите ссылку в директиве.

ПРИМЕЧАНИЕ: не забудьте добавить 'on-blur-val' именно таким образом в ваш элемент управления вводом без апострофов.


0

Используя ng-focus, вы можете достичь своей цели. вам нужно указать ng-focus в поле ввода. И при написании производных ng-show вы также должны написать логику не равную. Как приведенный ниже код:

<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus> <div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>


0

Мы можем использовать функции onfocus и onblur. Было бы просто и лучше.

<body ng-app="formExample">
  <div ng-controller="ExampleController">
  <form novalidate class="css-form">
    Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
    E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
  </form>
</div>

<style type="text/css">
 .css-form input.ng-invalid.ng-touched {
    border: 1px solid #FF0000;
    background:#FF0000;
   }
 .css-form input.focusOn.ng-invalid {
    border: 1px solid #000000;
    background:#FFFFFF;
 }
</style>

Попробуйте здесь:

http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.