Перевірити поля після того, як користувач залишив поле


92

За допомогою AngularJS я можу використовувати ng-pristineабо ng-dirtyвиявити, чи ввів користувач у поле. Однак я хочу зробити перевірку на стороні клієнта лише після того, як користувач покине область поля. Це пов’язано з тим, що коли користувач вводить таке поле, як електронна пошта або телефон, він завжди отримуватиме повідомлення про помилку, доки не завершить введення повного електронного листа, і це не є оптимальним для користувача досвідом.

Приклад


ОНОВЛЕННЯ:

Тепер Angular постачається із власною подією розмиття: https://docs.angularjs.org/api/ng/directive/ngBlur


3
Думаю, це angularjs мав би підтримувати ...
csg

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

11
перевірка розмитості поширена з 15 років ...
Olivvv,

Відповіді:


75

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

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'}"

І розвінчання

Ви можете затримати оновлення / перевірку моделі, використовуючи ключ розлучення з директивою ngModelOptions. Ця затримка також застосовуватиметься до синтаксичних аналізаторів, валідаторів та прапорів моделі, таких як $ брудний або $ первозданний.

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


2
У більшості випадків це не вирішує проблему, оскільки ви, мабуть, все одно хочете отримувати ng-changeподії до того, як користувач покине поле. Наприклад, у моєму випадку я запитую нові дані з мого API, як тільки поле введення є дійсним, але я не хочу показувати повідомлення про помилку, як тільки воно недійсне - я хочу робити це лише тоді, коли це недійсний і сталася подія розмиття.
Том,

@Tom я тестую, 1.3.0 rc-3і він не запускається changeдо blur, перевірте це: plnkr.co/edit/RivVU3r7xfHO1hUybqMu?p=preview
Gill Bates

Це справді має бути прийнятою, найкращою відповіддю. Навіщо додавати директиву, коли можна додати одну властивість?
Dan Hulton,

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'що означає, для входів, клавіші вгору та для
селекцій

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

3
@dudewad, це правильно, але я думаю, що це бажана поведінка, яка є розумною - якщо ви повернетесь до невірного поля, повідомлення про помилку повинно тривати доти, доки значення не буде дійсним, а не поки ви не введете першу літеру.
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 бути там, але чому знак питання?
Норемак

2
Дякую за ідею. Слід зазначити, що для цього потрібен jQuery (я думаю - не міг побачити .closest () у jql).
iandotkelly

1
Не всі елементи <input> підтримуються ngModel (наприклад, тип введення = подати). ? вимагає ngModel лише для елементів, які його підтримують.
chmanie

5
Чесно кажучи, вам не слід відхилятися від кутового способу встановлення станів поля введення, наприклад formName.inputName.$dirty. Я пропоную відредагувати директиву, щоб написати подібне логічне значення formName.inputName.$focused, а не використовувати імена класів для розмиття та логічні значення для перевірки.
Том,

17

Мені вдалося це зробити за допомогою досить простого фрагмента CSS. Це вимагає, щоб повідомлення про помилки були братами та сестрами вводу, до якого вони відносяться, і щоб вони мали клас error.

:focus ~ .error {
    display:none;
}

Після виконання цих двох вимог це приховає будь-яке повідомлення про помилку, пов’язане із зосередженим полем введення, - те, що, на мою думку, angularjs все одно має робити. Здається, недогляд.


2
Ага. Це розумний шлях. Я набагато більше люблю це, ніж написання javascript, щоб це зробити.
колода

5
Недоліком цього підходу є те, що коли хтось виправляє помилку, помилка більше не видно, що не є ідеальним. Ідеальним буде те, що помилка не відображається до розмиття, але не зникає, поки не буде виправлена.
Кріс Нікола

4

Здається, це впроваджено стандартно в нових версіях angular.

Класи ng-nedouched та 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-повідомлень (доступних у 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

Використовувати стан поля $ touch Поле було торкнуто для цього, як показано в прикладі нижче.

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

0

Ви можете динамічно встановити клас css has-error (припускаючи, що ви використовуєте bootstrap), використовуючи ng-клас та властивість в області асоційованого контролера:

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 і менше css, ви можете ввімкнути перевірку розмитості за допомогою такого менш фрагмента:

: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, де відображається моє повідомлення про перевірку, і тому директива як атрибут додається до кожного елемента керування входом.

У мене також є (необов’язково) .has-focus та відвіданий клас css у моєму файлі css, на який ви бачите посилання в директиві.

ПРИМІТКА: не забудьте додати 'on-blur-val' саме таким чином до вашого елемента управління введенням без апострофів


0

За допомогою ng-focus ви можете досягти своєї мети. вам потрібно вказати ng-фокус у полі введення. І під час написання своїх похідних 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.