Як додати спеціальну перевірку до форми AngularJS?


278

У мене є форма з полями введення та налаштуванням перевірки, додаючи requiredатрибути та ін. Але для деяких полів мені потрібно зробити додаткову перевірку. Як я можу "скористатися" валідацією, яка FormControllerконтролює?

Перевірка на замовлення може бути чимось на кшталт "якщо ці 3 поля заповнені, то це поле обов'язкове та має бути відформатоване певним чином".

Існує метод, FormController.$setValidityале він не схожий на загальнодоступний API, тому я його краще не використовую. Створення власної директиви та використання NgModelControllerвиглядає як інший варіант, але в основному вимагає від мене створення директиви для кожного користувацького правила перевірки, чого я не хочу.

Насправді, позначення поля від контролера як недійсного (при цьому також FormControllerсинхронізується) може бути те, що мені потрібно в найпростішому сценарії, щоб виконати роботу, але я не знаю, як це зробити.


4
Є чудова стаття про кодування монстра для обробки користувацьких перевірок у кутовій JS. Перевірте це аут
Anshu

Я не саме те, що я шукаю, оскільки для нього потрібні спеціальні директиви, але я прийму вашу відповідь, оскільки це все-таки хороша стаття.
botteaap

Мені цікаво те саме, я хотів би трохи контролювати на рівні FormController. Наприклад, я хочу, щоб певні спеціальні директиви позначили екземпляр FormController як щось подібне formName.$warning.
Адам Васельнюк

2
Я вважаю, що це $$передує неофіційним апісам, $оскільки вони публічні. Дивіться stackoverflow.com/questions/19338493/…
Даніель F

Відповіді:


370

Редагувати: додано нижче інформацію про ngПовідомлення (> = 1.3.X).

Повідомлення для перевірки стандартної форми (1.0.X і вище)

Оскільки це один із найкращих результатів, якщо ви "Google" Перевірка кутових форм ", я хочу додати ще одну відповідь на це для всіх, хто заходить звідти.

У FormController є метод. $ SetValidity, але він не схожий на загальнодоступний API, тому я скоріше не використовую його.

Це "публічно", ніяких турбот. Використай це. Ось для чого це. Якби його не мали використовувати, кутові розробники приватизували б його в закритому порядку.

Щоб виконати спеціальну перевірку, якщо ви не хочете використовувати Angular-UI як інша відповідь, запропоновану відповідь, ви можете просто прокатати власну директиву перевірки.

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {
          var blacklist = attr.blacklist.split(',');

          //For DOM -> model validation
          ngModel.$parsers.unshift(function(value) {
             var valid = blacklist.indexOf(value) === -1;
             ngModel.$setValidity('blacklist', valid);
             return valid ? value : undefined;
          });

          //For model -> DOM validation
          ngModel.$formatters.unshift(function(value) {
             ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
             return value;
          });
      }
   };
});

Ось кілька прикладів використання:

<form name="myForm" ng-submit="doSomething()">
   <input type="text" name="fruitName" ng-model="data.fruitName" blacklist="coconuts,bananas,pears" required/>
   <span ng-show="myForm.fruitName.$error.blacklist">
      The phrase "{{data.fruitName}}" is blacklisted</span>
   <span ng-show="myForm.fruitName.$error.required">required</span>
   <button type="submit" ng-disabled="myForm.$invalid">Submit</button>
</form>

Примітка: в 1.2.X це, ймовірно , preferrable замінити ng-ifна ng-showвище

Ось обов’язкове посилання на планкер

Крім того, я написав кілька записів у блозі про цю тему, яка детальніше описується:

Перевірка кутової форми

Спеціальні директиви щодо валідації

Редагувати: використовуючи ngMessages в 1.3.X

Тепер ви можете використовувати модуль ngMessages замість ngShow для показу повідомлень про помилки. Він фактично працюватиме з чим завгодно, це не повинно бути повідомлення про помилку, але ось основні принципи:

  1. Включати <script src="angular-messages.js"></script>
  2. Посилання ngMessagesв декларації модуля:

    var app = angular.module('myApp', ['ngMessages']);
  3. Додати відповідну розмітку:

    <form name="personForm">
      <input type="email" name="email" ng-model="person.email" required/>
    
      <div ng-messages="personForm.email.$error">
        <div ng-message="required">required</div>
        <div ng-message="email">invalid email</div>
      </div>
    </form>

У вищенаведеній розмітці в ng-message="personForm.email.$error"основному вказується контекст для ng-messageдочірніх директив. Потім ng-message="required"і ng-message="email"вкажіть властивості в цьому контексті для перегляду. Найголовніше, що вони також вказують наказ про їх реєстрацію . Перший, який він знайде у списку, який є "правдоподібним", виграє, і він покаже це повідомлення і ніхто з інших.

І планкер для прикладу ngMessages


6
Якщо ви повернете значення для функції, яку ви передаєте на $ parsers.unshift, помилкові значення також будуть збережені в моделі - було б краще повернути невизначені, я вважаю (коли значення недійсне).
georgiosd

5
+1 @georgiosd ... 100% вірно. Переглядаючи, що робить Angular, вони повертаються невизначеними. Повернути значення, мабуть, не так вже й складно , оскільки (сподіваємось) моделі з недійсних форм не подаються ... але, мабуть, безпечніше, ніж вибачте.
Бен Леш

2
Чудові речі! Якщо ви заглянули в Google, шукаючи хорошу підписку на спеціальну перевірку в Angular, перегляньте, що написав
@blesh

Ви перевірили розширену перевірку форми за допомогою AngularJS та фільтрів ? Він вирішує перевірку фільтра загально.
Бенні Боттема

1
Я думаю, ви, можливо, мали намір зробити return value ? valid : undefinedвище.
GChorn

92

Проект Angular-UI включає директиву ui-validate, яка, ймовірно, допоможе вам у цьому. Давайте ви вкажете функцію для виклику для проведення перевірки.

Перегляньте демонстраційну сторінку: http://angular-ui.github.com/ , пошукайте вниз до заголовка «Перевірити».

На демонстраційній сторінці:

<input ng-model="email" ui-validate='{blacklist : notBlackListed}'>
<span ng-show='form.email.$error.blacklist'>This e-mail is black-listed!</span>

потім у своєму контролері:

function ValidateCtrl($scope) {
  $scope.blackList = ['bad@domain.com','verybad@domain.com'];
  $scope.notBlackListed = function(value) {
    return $scope.blackList.indexOf(value) === -1;
  };
}

Як не дивно, що це не працює для мене, використовуючи Angular 1.4
Нік

46

Ви можете використовувати ng-необхідний для свого сценарію перевірки ("якщо ці 3 поля заповнені, тоді це поле обов'язкове":

<div ng-app>
    <input type="text" ng-model="field1" placeholder="Field1">
    <input type="text" ng-model="field2" placeholder="Field2">
    <input type="text" ng-model="field3" placeholder="Field3">
    <input type="text" ng-model="dependentField" placeholder="Custom validation"
        ng-required="field1 && field2 && field3">
</div>

2
Це працювало для мене. Для простих перевірок, що залежать від інших значень полів, це шлях, а не написання складних правил перевірки
VimalKumar

28

Можна використовувати Angular-Validator .

Приклад: використання функції для перевірки поля

<input  type = "text"
    name = "firstName"
    ng-model = "person.firstName"
    validator = "myCustomValidationFunction(form.firstName)">

Тоді у вашому контролері ви мали б щось подібне

$scope.myCustomValidationFunction = function(firstName){ 
   if ( firstName === "John") {
       return true;
    }

Ви також можете зробити щось подібне:

<input  type = "text"
        name = "firstName"
        ng-model = "person.firstName"
        validator = "'!(field1 && field2 && field3)'"
        invalid-message = "'This field is required'">

(де field1 field2 та field3 - змінні області. Ви також можете перевірити, чи поля не відповідають порожній рядку)

Якщо поле не проходить, validatorто поле буде позначене як недійсне і користувач не зможе надіслати форму.

Більше випадків використання та прикладів див: https://github.com/turinggroup/angular-validator

Відмова: Я автор Angular-Validator


13

Нещодавно я створив директиву, щоб дозволити недійсне введення кутових форм на основі вираження. Можна використовувати будь-яке дійсне кутове вираження, яке підтримує власні ключі перевірки, використовуючи позначення об'єкта. Випробуваний з кутовим v1.3.8

        .directive('invalidIf', [function () {
        return {
            require: 'ngModel',
            link: function (scope, elm, attrs, ctrl) {

                var argsObject = scope.$eval(attrs.invalidIf);

                if (!angular.isObject(argsObject)) {
                    argsObject = { invalidIf: attrs.invalidIf };
                }

                for (var validationKey in argsObject) {
                    scope.$watch(argsObject[validationKey], function (newVal) {
                        ctrl.$setValidity(validationKey, !newVal);
                    });
                }
            }
        };
    }]);

Ви можете використовувати його так:

<input ng-model="foo" invalid-if="{fooIsGreaterThanBar: 'foo > bar',
                                   fooEqualsSomeFuncResult: 'foo == someFuncResult()'}/>

Або просто передавши вираз (йому буде надано валідацію за замовчуванням Ключ "invalidIf")

<input ng-model="foo" invalid-if="foo > bar"/>

13

Ось класний спосіб зробити спеціальну перевірку виражених підстановок у формі (з: Розширена перевірка форми за допомогою AngularJS та фільтрів ):

<form novalidate="">  
   <input type="text" id="name" name="name" ng-model="newPerson.name"
      ensure-expression="(persons | filter:{name: newPerson.name}:true).length !== 1">
   <!-- or in your case:-->
   <input type="text" id="fruitName" name="fruitName" ng-model="data.fruitName"
      ensure-expression="(blacklist | filter:{fruitName: data.fruitName}:true).length !== 1">
</form>
app.directive('ensureExpression', ['$http', '$parse', function($http, $parse) {
    return {
        require: 'ngModel',
        link: function(scope, ele, attrs, ngModelController) {
            scope.$watch(attrs.ngModel, function(value) {
                var booleanResult = $parse(attrs.ensureExpression)(scope);
                ngModelController.$setValidity('expression', booleanResult);
            });
        }
    };
}]);

Демонстрація jsFiddle (підтримує іменування виразів та декілька виразів)

Це схоже на ui-validate, але вам не потрібна функція перевірки специфіки для області застосування (це працює загалом), і, звичайно, вам не потрібні ui.utils таким чином.


Дякую. Дуже круто. Особливо корисно застосовувати правила перевірки динамічних форм. Однак воно все одно встановлює значення моделі, навіть якщо воно недійсне. У будь-якому випадку, щоб запобігти встановленню modelValue, якщо він недійсний?
ЮМей

5

Оновлення:

Удосконалена та спрощена версія попередньої директиви (одна замість двох) з однаковою функціональністю:

.directive('myTestExpression', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            var expr = attrs.myTestExpression;
            var watches = attrs.myTestExpressionWatch;

            ctrl.$validators.mytestexpression = function (modelValue, viewValue) {
                return expr == undefined || (angular.isString(expr) && expr.length < 1) || $parse(expr)(scope, { $model: modelValue, $view: viewValue }) === true;
            };

            if (angular.isString(watches)) {
                angular.forEach(watches.split(",").filter(function (n) { return !!n; }), function (n) {
                    scope.$watch(n, function () {
                        ctrl.$validate();
                    });
                });
            }
        }
    };
}])

Приклад використання:

<input ng-model="price1" 
       my-test-expression="$model > 0" 
       my-test-expression-watch="price2,someOtherWatchedPrice" />
<input ng-model="price2" 
       my-test-expression="$model > 10" 
       my-test-expression-watch="price1" 
       required />

Результат: Взаємозалежні тестові вирази, коли валідатори виконуються на зміну моделі директиви та поточної моделі.

Тестовий вираз має локальну $modelзмінну, яку слід використовувати для порівняння з іншими змінними.

Раніше:

Я зробив спробу поліпшити @Plantface код, додавши додаткову директиву. Ця додаткова директива дуже корисна, якщо наше вираження потрібно виконати, коли зміни вносяться в більш ніж одну змінну ngModel.

.directive('ensureExpression', ['$parse', function($parse) {
    return {
        restrict: 'A',
        require: 'ngModel',
        controller: function () { },
        scope: true,
        link: function (scope, element, attrs, ngModelCtrl) {
            scope.validate = function () {
                var booleanResult = $parse(attrs.ensureExpression)(scope);
                ngModelCtrl.$setValidity('expression', booleanResult);
            };

            scope.$watch(attrs.ngModel, function(value) {
                scope.validate();
            });
        }
    };
}])

.directive('ensureWatch', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        require: 'ensureExpression',
        link: function (scope, element, attrs, ctrl) {
            angular.forEach(attrs.ensureWatch.split(",").filter(function (n) { return !!n; }), function (n) {
                scope.$watch(n, function () {
                    scope.validate();
                });
            });
        }
    };
}])

Приклад, як використовувати його для складання перехресних перевірених полів:

<input name="price1"
       ng-model="price1" 
       ensure-expression="price1 > price2" 
       ensure-watch="price2" />
<input name="price2" 
       ng-model="price2" 
       ensure-expression="price2 > price3" 
       ensure-watch="price3" />
<input name="price3" 
       ng-model="price3" 
       ensure-expression="price3 > price1 && price3 > price2" 
       ensure-watch="price1,price2" />

ensure-expressionвиконується для перевірки моделі при зміні ng-modelбудь-якої зі ensure-watchзмінних.


4

@synergetic Я думаю, що @blesh припустимо поставити функцію валідацією, як показано нижче

function validate(value) {
    var valid = blacklist.indexOf(value) === -1;
    ngModel.$setValidity('blacklist', valid);
    return valid ? value : undefined;
}

ngModel.$formatters.unshift(validate);
ngModel.$parsers.unshift(validate);

4

Спеціальні перевірки, які викликають Сервер

Використовуйте API ngModelController,$asyncValidators який обробляє асинхронну перевірку, наприклад, $httpзапит до бекенда. Функції, додані до об'єкта, повинні повертати обіцянку, яка повинна бути вирішена, коли вона дійсна або відхилена, коли недійсна. Перевірки асинхронизації, що триває, зберігаються ключем у ngModelController.$pending. Для отримання додаткової інформації див. Посібник для розробників AngularJS - форми (спеціальна перевірка) .

ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
  var value = modelValue || viewValue;

  // Lookup user by username
  return $http.get('/api/users/' + value).
     then(function resolved() {
       //username exists, this means validation fails
       return $q.reject('exists');
     }, function rejected() {
       //username does not exist, therefore this validation passes
       return true;
     });
};

Для отримання додаткової інформації див


Використання $validatorsAPI

Прийнята відповідь використовує $parsersі $formattersконвеєри, щоб додати спеціальний синхронний валідатор. AngularJS 1.3+ додав $validatorsAPI , так що немає необхідності ставити валідатор в $parsersі $formattersтрубопроводах:

app.directive('blacklist', function (){ 
   return {
      require: 'ngModel',
      link: function(scope, elem, attr, ngModel) {           
          ngModel.$validators.blacklist = function(modelValue, viewValue) {
              var blacklist = attr.blacklist.split(',');
              var value = modelValue || viewValue;
              var valid = blacklist.indexOf(value) === -1;
              return valid;
          });    
      }
   };
});

Для отримання додаткової інформації див. Довідник API AngularJS ngModelController - валідатори $ .


3

У AngularJS найкращим місцем для визначення користувальницької перевірки є директива Cutsom. AngularJS забезпечують модуль ngMessages.

ngMessages - це директива, призначена для показу та приховування повідомлень на основі стану об'єкта ключ / значення, який він слухає. Сама директива доповнює звітність про помилку з об’єктом помилки ngModel $ (який зберігає стан ключа / значення помилок перевірки).

Для перевірки користувальницької форми слід використовувати модулі ngMessages з власною директивою. Ось у мене є проста перевірка, яка перевірить, чи довжина номера менше 6, на екрані відображається помилка

 <form name="myform" novalidate>
                <table>
                    <tr>
                        <td><input name='test' type='text' required  ng-model='test' custom-validation></td>
                        <td ng-messages="myform.test.$error"><span ng-message="invalidshrt">Too Short</span></td>
                    </tr>
                </table>
            </form>

Ось як створити власну директиву перевірки

angular.module('myApp',['ngMessages']);
        angular.module('myApp',['ngMessages']).directive('customValidation',function(){
            return{
            restrict:'A',
            require: 'ngModel',
            link:function (scope, element, attr, ctrl) {// 4th argument contain model information 

            function validationError(value) // you can use any function and parameter name 
                {
                 if (value.length > 6) // if model length is greater then 6 it is valide state
                 {
                 ctrl.$setValidity('invalidshrt',true);
                 }
                 else
                 {
                 ctrl.$setValidity('invalidshrt',false) //if less then 6 is invalide
                 }

                 return value; //return to display  error 
                }
                ctrl.$parsers.push(validationError); //parsers change how view values will be saved in the model
            }
            };
        });

$setValidity є вбудованою функцією для встановлення стану моделі на дійсне / недійсне


1

Я розширив відповідь @Ben Lesh із можливістю уточнювати, чи відповідає валідація регістру чи ні (за замовчуванням)

використання:

<input type="text" name="fruitName" ng-model="data.fruitName" blacklist="Coconuts,Bananas,Pears" caseSensitive="true" required/>

код:

angular.module('crm.directives', []).
directive('blacklist', [
    function () {
        return {
            restrict: 'A',
            require: 'ngModel',
            scope: {
                'blacklist': '=',
            },
            link: function ($scope, $elem, $attrs, modelCtrl) {

                var check = function (value) {
                    if (!$attrs.casesensitive) {
                        value = (value && value.toUpperCase) ? value.toUpperCase() : value;

                        $scope.blacklist = _.map($scope.blacklist, function (item) {
                            return (item.toUpperCase) ? item.toUpperCase() : item
                        })
                    }

                    return !_.isArray($scope.blacklist) || $scope.blacklist.indexOf(value) === -1;
                }

                //For DOM -> model validation
                modelCtrl.$parsers.unshift(function (value) {
                    var valid = check(value);
                    modelCtrl.$setValidity('blacklist', valid);

                    return value;
                });
                //For model -> DOM validation
                modelCtrl.$formatters.unshift(function (value) {
                    modelCtrl.$setValidity('blacklist', check(value));
                    return value;
                });
            }
        };
    }
]);

0

У цій темі представлено кілька чудових прикладів та ліфтів, але у них було зовсім не те, що я шукав. Мій підхід: кутова-валідність - вкладка перевірки на основі обіцянки для асинхронної перевірки з додатковою формою Bootstrap.

Рішення з кутовою дійсністю для випадку використання ОП може виглядати приблизно так:

<input  type="text" name="field4" ng-model="field4"
        validity="eval"
        validity-eval="!(field1 && field2 && field3 && !field4)"
        validity-message-eval="This field is required">

Ось Скрипка , якщо ви хочете взяти її за спину. Ліб доступний на GitHub , має детальну документацію та безліч демонстрацій.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.