Как добавить пользовательскую проверку в форму AngularJS?
у меня есть форма с полями ввода и настройки проверки, добавив required атрибуты и тому подобное. Но для некоторых полей мне нужно сделать дополнительную проверку. Как бы я "подключился" к проверке, что FormController контроля?
пользовательская проверка может быть чем-то вроде "если эти 3 поля заполнены, то это поле является обязательным и должно быть отформатировано определенным образом".
есть метод в FormController.$setValidity но это не похоже на публичный API, поэтому я скорее не использовать его. Создание пользовательской директивы и использование NgModelController похоже на другой вариант, но в основном мне потребуется создать директиву для каждого пользовательского правила проверки, которое я не хочу.
на самом деле, маркировка поля из контроллера как недопустимый (в то же время сохраняя FormController in sync) может быть то, что мне нужно в самом простом сценарии, чтобы выполнить эту работу, но я не знаю, как это сделать.
12 ответов:
Edit: добавлена информация о ngMessages (>= 1.3.Х) ниже.
стандартные сообщения проверки формы (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 вероятно, предпочтительнее заменить
ng-ifибоng-showвышевот обязательное plunker link
кроме того, я написал несколько записей в блоге только об этой теме, которая переходит в немного более подробно:
Пользовательские Директивы Проверки
Edit: использование ngMessages в 1.3.X
теперь вы можете использовать модуль ngMessages вместо ngShow, чтобы показать свою ошибку сообщения. Это будет работать с чем угодно, это не обязательно должно быть сообщение об ошибке, но вот основы:
- включить
<script src="angular-messages.js"></script>ссылка
ngMessagesв объявлении модуля:var app = angular.module('myApp', ['ngMessages']);добавьте соответствующую разметку:
<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"указать свойства в этом контексте для просмотра. самое главное, они также указывают порядок их регистрации. Первый, который он находит в списке, который является "правдивым", выигрывает, и он покажет это сообщение и ни одно из других.
проект Angular-UI включает директиву ui-validate, которая, вероятно, поможет вам в этом. Это позволит вам указать функцию для вызова, чтобы сделать проверку.
посмотрите на демо-страницу:http://angular-ui.github.com/, выполните поиск до заголовка Validate.
с демонстрационной страницы:
<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 = ['[email protected]','[email protected]']; $scope.notBlackListed = function(value) { return $scope.blackList.indexOf(value) === -1; }; }
вы можете использовать ng-required для вашего сценария проверки ("если эти 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>
можно использовать Угловое-Оценщику.
пример: использование функции для проверки поля
<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
вот классный способ сделать пользовательские подстановочные выражения проверки в форме (от:Расширенная проверка формы с помощью 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.утилиты этот путь.
недавно я создал директиву, чтобы разрешить недействительность угловых входных форм на основе выражений. Можно использовать любое допустимое угловое выражение, и оно поддерживает пользовательские ключи проверки с использованием нотации объектов. Протестировано с угловой 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"/>
@ 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);
обновление:
улучшенная и упрощенная версия предыдущей директивы (одна вместо двух) с той же функциональностью:
.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переменные изменяются.
в AngularJS лучшим местом для определения пользовательской проверки является директива Cutsom. AngularJS обеспечивают модуль ngMessages.
ngmessages-это директива, предназначенная для отображения и скрытия сообщений на основе состояния объекта ключа / значения, который он прослушивает. Этот сама директива дополняет отчет об ошибках с помощью ngModel $error object (который хранит состояние ключа/значения ошибок проверки).
для проверки пользовательской формы следует используйте модули 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- это встроенная функция, чтобы установить модель государства для действительных/недействительных
я расширил ответ @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; }); } }; } ]);
пользовательские проверки, которые вызывают сервер
использовать ngModelController
$asyncValidatorsAPI который обрабатывает асинхронную проверку, например, делает$httpзапрос к backend. Функции, добавленные к объекту, должны возвращать обещание, которое должно быть разрешено при допустимом или отклонено при недопустимом. Выполняемые асинхронные проверки хранятся по ключу в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; }); };для получения дополнительной информации см. раздел
некоторые великие примеры и библиотеки, представленные в этой теме, но они не совсем то, что я искал. Мой подход: угловое-действительность -- Promise based validation lib для асинхронной проверки, с дополнительным встроенным стилем Bootstrap.
решение угловой валидности для варианта использования OP может выглядеть примерно так:
<input type="text" name="field4" ng-model="field4" validity="eval" validity-eval="!(field1 && field2 && field3 && !field4)" validity-message-eval="This field is required">здесь Скрипка, Если вы хотите взять его на спину. Библиотека доступна на GitHub, имеет подробную документацию,и много живых демо.
Comments