Сравнение двух входных значений в форме проверки с помощью AngularJS
Я пытаюсь выполнить проверку формы с помощью AngularJS. Меня особенно интересует сравнение двух значений. Я хочу, чтобы пользователь подтвердил некоторые данные, которые он ввел до его продолжения. Допустим, у меня есть код ниже:
<p>
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2">
<p>
а затем я могу использовать валидацию с помощью:
<span ng-show="registerForm.email1.$error.required">Required!</span>
<span ng-show="registerForm.email1.$error.email">Not valid email!</span>
<span ng-show="emailReg !== emailReg2">Emails have to match!</span> <-- see this line
registerForm. $valid будет корректно реагировать на текст в входе, за исключением того, что я не знаю, как использовать сравнение в рамках этой проверки, чтобы заставить электронные письма быть одинаковыми, прежде чем разрешить пользователю отправлять форму.
Я хотел бы иметь решение без пользовательских директив, но если этого не достичь без него, я буду иметь дело с ним. Здесь - ответ, который касается аналогичной проблемы с пользовательской директивой.
Любая помощь приветствуется, спасибо
Ответы
Ответ 1
Один из способов добиться этого - с помощью специальной директивы. Здесь пример использования пользовательской директивы (ng-match
в этом случае):
<p>Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-match="emailReg"></p>
<span data-ng-show="myForm.emailReg2.$error.match">Emails have to match!</span>
ПРИМЕЧАНИЕ. Обычно не рекомендуется использовать ng-
в качестве префикса для пользовательской директивы, поскольку это может противоречить официальной директиве AngularJS.
Обновить
Также возможно получить эту функциональность без использования настраиваемой директивы:
HTML
<button ng-click="add()></button>
<span ng-show="IsMatch">Emails have to match!</span>
контроллер
$scope.add = function() {
if ($scope.emailReg != $scope.emailReg2) {
$scope.IsMatch=true;
return false;
}
$scope.IsMatch=false;
}
Ответ 2
Вы можете использовать ng-pattern/regex для сравнения 2 входных значений
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">
и подтверждение с помощью:
<span ng-show="registerForm.email2.$error.pattern">Repeat Email should have the same value with email!</span>
Ответ 3
trainosais - вы правы, валидация должна проводиться на уровне директивы. Он чистый, модульный и позволяет повторно использовать код. Когда у вас есть базовое подтверждение, подобное этому в контроллере, вы пишете его снова и снова для разных форм. Этот супер-сухой.
У меня была аналогичная проблема в последнее время и отсортировано с помощью простой директивы, которая подключается к конвейеру парсеров, поэтому остается совместимой с архитектурой Angular. Цепочные валидаторы делают его очень простым для повторного использования, и это должно рассматриваться как единственное решение на мой взгляд.
Без дальнейших церемоний здесь упрощенная разметка:
<form novalidate="novalidate">
<label>email</label>
<input type="text"
ng-model="email"
name="email" />
<label>email repeated</label>
<input ng-model="emailRepeated"
same-as="email"
name="emailRepeated" />
</form>
И код JS:
angular.module('app', [])
.directive('sameAs', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$parsers.unshift(validate);
// Force-trigger the parsing pipeline.
scope.$watch(attrs.sameAs, function() {
ngModel.$setViewValue(ngModel.$viewValue);
});
function validate(value) {
var isValid = scope.$eval(attrs.sameAs) == value;
ngModel.$setValidity('same-as', isValid);
return isValid ? value : undefined;
}
}
};
});
Директива перехватывает конвейер парсеров, чтобы получать уведомления о любых изменениях в представлении и устанавливать достоверность на основе сравнения нового значения представления и значения ссылочного поля. Этот бит прост. Трудный бит обнюхивает изменения в поле ссылки. Для этого директива устанавливает Бодрствующего на эталонное значение и сила-triggeres трубопровод синтаксического анализа, для того, чтобы получить все валидаторы снова запустить.
Если вы хотите поиграть с ним, вот моя ручка:
http://codepen.io/jciolek/pen/kaKEn
Надеюсь, это поможет,
Яцек
Ответ 4
Недавно я написал настраиваемую директиву, которая может быть достаточно общей для любой проверки. Он принимает функцию проверки из текущей области
module.directive('customValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
scope: { validateFunction: '&' },
link: function (scope, elm, attr, ngModelCtrl) {
ngModelCtrl.$parsers.push(function (value) {
var result = scope.validateFunction({ 'value': value });
if (result || result === false) {
if (result.then) {
result.then(function (data) { //For promise type result object
ngModelCtrl.$setValidity(attr.customValidator, data);
}, function (error) {
ngModelCtrl.$setValidity(attr.customValidator, false);
});
}
else {
ngModelCtrl.$setValidity(attr.customValidator, result);
return result ? value : undefined; //For boolean result return based on boolean value
}
}
return value;
});
}
};
}]);
Чтобы использовать его, вы делаете
<input type="email" name="email2" ng-model="emailReg2" custom-validator='emailMatch' data-validate-function='checkEmailMatch(value)'>
<span ng-show="registerForm.email2.$error.emailMatch">Emails have to match!</span>
В вашем контроллере вы можете реализовать метод, который должен возвращать true или false
$scope.checkEmailMatch=function(value) {
return value===$scope.emailReg;
}
Преимущество состоит в том, что вам не нужно писать настраиваемую директиву для каждой пользовательской проверки.
Ответ 5
При обновлении angular до 1.3 и выше я нашел проблему с Jacek Ciolek отличным ответом:
- Добавить данные в поле ссылки
- Добавьте те же данные в поле с директивой по нему (это поле теперь действительно)
- Вернитесь в поле ссылки и измените данные (поле директивы остается в силе)
Я протестировал ответ rdukeshier (обновление var modelToMatch = element.attr('sameAs')
до var modelToMatch = attrs.sameAs
для правильной загрузки эталонной модели), но та же проблема возникла.
Чтобы исправить это (проверено в angular 1.3 и 1.4), я адаптировал код rdukeshier и добавил наблюдателя в поле ссылки для запуска всех проверок при изменении ссылочного поля. Теперь директива выглядит следующим образом:
angular.module('app', [])
.directive('sameAs', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
var modelToMatch = attrs.sameAs;
scope.$watch(attrs.sameAs, function() {
ctrl.$validate();
})
ctrl.$validators.match = function(modelValue, viewValue) {
return viewValue === scope.$eval(modelToMatch);
};
}
};
});
Обновлен codepen
Ответ 6
используйте ng-pattern, так что ng-valid и ng-dirty могут действовать правильно
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">
<span ng-show="registerForm.email2.$error.pattern">Emails have to match!</span>
Ответ 7
Нет необходимости в функции или директиве. Просто сравните их $modelValue с представлением:
ng-show="formName.email.$modelValue !== formName.confirmEmail.$modelValue"
Более подробный пример:
<span ng-show="(formName.email.$modelValue !== formName.confirmEmail.$modelValue)
&& formName.confirmEmail.$touched
&& !formName.confirmEmail.$error.required">Email does not match.</span>
Обратите внимание на, что ConfirmEmail находится за пределами ViewModel; это свойство $scope. Его не нужно подавать.
Ответ 8
Метод Генри-Нео был близок, ему просто нужны более строгие правила регулярных выражений.
<form name="emailForm">
Email: <input type="email" name="email1" ng-model="emailReg">
Repeat Email: <input type="email" name="email2" ng-model="emailReg2" ng-pattern="(emailReg)">
</form>
Включая правило регулярных выражений в круглых скобках, оно будет соответствовать всей строке от emailReg
до emailReg2
и приведет к сбою проверки формы, потому что она не соответствует.
Затем вы можете просверлить элементы, чтобы узнать, какая часть не работает.
<p ng-show="emailForm.$valid">Form Valid</p>
<p ng-show="emailForm.email1.$error">Email not valid</p>
<p ng-show="emailForm.email1.$valid && emailForm.email1.$error.pattern">
Emails Do Not Match
</p>
Ответ 9
Этот модуль хорошо работает для сравнения двух полей. Отлично работает с Angular 1.3+. Простой в использовании
https://www.npmjs.com/package/angular-password
Он также позволяет сохранить модуль как общий. Просто включите их в список пакетов вашего модуля.
Ответ 10
Вот моя простая версия настраиваемой директивы валидатора:
angular.module('app')
.directive('equalsTo', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
scope.$watchGroup([attrs.equalsTo, () => ngModel.$modelValue], newVal => {
ngModel.$setValidity('equalsTo', newVal[0] === newVal[1]);
});
}
};
})
Ответ 11
Ниже приведена версия angular 1.3 директивы sameAs:
angular.module('app').directive('sameAs', [function() {
'use strict';
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
var modelToMatch = element.attr('sameAs');
ctrl.$validators.match = function(modelValue, viewValue) {
return viewValue === scope.$eval(modelToMatch);
};
}
};
}]);
Ответ 12
Мина похожа на ваше решение, но я получил ее на работу. Единственное отличие - моя модель. У меня есть следующие модели в моем html-вводе:
ng-model="new.Participant.email"
ng-model="new.Participant.confirmEmail"
и в моем контроллере у меня это в $scope:
$scope.new = {
Participant: {}
};
и эта строка проверки работала:
<label class="help-block" ng-show="new.Participant.email !== new.Participant.confirmEmail">Emails must match! </label>
Ответ 13
Спасибо за отличный пример @Jacek Ciolek. Для angular 1.3.x это решение ломается, когда к исходному входному значению добавляются обновления. Основываясь на этом примере для angular 1.3.x, это решение работает также с angular 1.3.x. Он связывает и наблюдает за изменениями эталонного значения.
angular.module('app', []).directive('sameAs', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
sameAs: '='
},
link: function(scope, elm, attr, ngModel) {
if (!ngModel) return;
attr.$observe('ngModel', function(value) {
// observes changes to this ngModel
ngModel.$validate();
});
scope.$watch('sameAs', function(sameAs) {
// watches for changes from sameAs binding
ngModel.$validate();
});
ngModel.$validators.sameAs = function(value) {
return scope.sameAs == value;
};
}
};
});
Вот мое перо: http://codepen.io/kvangrae/pen/BjxMWR
Ответ 14
Вы должны посмотреть на большую проблему. Как написать директивы, которые решают одну проблему. Вы должны попробовать директиву use-form-error. Помогло бы оно решить эту проблему и многие другие.
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required />
<div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>
Пример в реальном времени jsfiddle
Ответ 15
Мне нужно сделать это только в одной форме во всем моем приложении, и я вижу директиву, такую как супер-сложный для моего случая, поэтому я использую ng-patter
, как некоторые, имеет точку, но имеет некоторые проблемы, когда строка имеет специальные символы, такие как .[\
, это сломалось, поэтому я создаю функцию для специальных символов scape.
$scope.escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
и в представлении
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required ng-pattern="escapeRegExp(password)"/>
</form>
Ответ 16
Конечно, для очень простых сравнений вы всегда можете использовать ngMin
/ngMax
.
В противном случае вы можете перейти с пользовательской директивой, и не нужно делать любые $watch
или $observe
или $eval
или этот причудливый $setValidity
взад и вперед. Кроме того, нет необходимости вообще подключаться к функции postLink. Постарайтесь держаться подальше от DOM как можно больше, поскольку это против духа angular.
Просто используйте привязки жизненного цикла, которые дает вам фреймворк. Добавьте валидатор и $validate
при каждом изменении. Просто как это.
app.directive('sameAs', function() {
return {
restrict: 'A',
require: {
ngModelCtrl: 'ngModel'
},
scope: {
reference: '<sameAs'
},
bindToController: true,
controller: function($scope) {
var $ctrl = $scope.$ctrl;
//add the validator to the ngModelController
$ctrl.$onInit = function() {
function sameAsReference (modelValue, viewValue) {
if (!$ctrl.reference || !modelValue) { //nothing to compare
return true;
}
return modelValue === $ctrl.reference;
}
$ctrl.ngModelCtrl.$validators.sameas = sameAsReference;
};
//do the check at each change
$ctrl.$onChanges = function(changesObj) {
$ctrl.ngModelCtrl.$validate();
};
},
controllerAs: '$ctrl'
};
});
Ваш plunker.
Ответ 17
Я модифицировал метод Chandermani для совместимости с Angularjs 1.3 и upper. Перенесено из $ parsers в $ asyncValidators.
module.directive('customValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
scope: { validateFunction: '&' },
link: function (scope, elm, attr, ngModelCtrl) {
ngModelCtrl.$asyncValidators[attr.customValidator] = function (modelValue, viewValue) {
return new Promise(function (resolve, reject) {
var result = scope.validateFunction({ 'value': viewValue });
if (result || result === false) {
if (result.then) {
result.then(function (data) { //For promise type result object
if (data)
resolve();
else
reject();
}, function (error) {
reject();
});
}
else {
if (result)
resolve();
else
reject();
return;
}
}
reject();
});
}
}
};
}]);
Использование одинаковое