Skip to content

Commit

Permalink
Merge pull request #175 from lvarayut/validation-group
Browse files Browse the repository at this point in the history
Support validation group
  • Loading branch information
Huei Tan committed Dec 27, 2015
2 parents 41a40ae + 4862114 commit df9f4d4
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 36 deletions.
14 changes: 14 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ You can also add a custom validation message by using `message-id` attribute. It
<span id="message"></span>
```

### **Use a validation group** <br/>
You can also add a `validation-group` directive to group many elements into a group. The group will be considered as valid if and only if one of them is valid. Otherwise, the group will be marked as invalid. A valid/invalid message will be placed inside an element that contains an id attribute with the same name as provided to the directive `validation-group`.

```html
<label>Validation group</label>
<!-- Group both of these elements inside the contact group -->
<input type="text" name="email" ng-model="email" validator="required" validation-group="contact">
<input type="number" name="telephone" ng-model="telephone" validator="number" validation-group="contact">
<!-- The message will be placed in side the span element -->
<span id="contact"></span>
```

> Note that the `validation-group` directive can only be used to group elements placed in the same form. In other words, you can't group elements across different `<form>` tags.
<a name="no-validation-message"></a>
### **Don't show the Valid Message `no-validation-message="true"`**

Expand Down
78 changes: 62 additions & 16 deletions dist/angular-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,11 @@
var validFunc = function(element, validMessage, validation, scope, ctrl, attrs) {
var messageToShow = validMessage || $validationProvider.getDefaultMsg(validation).success;
var validCallback = $parse('success');
var messageId = attrs.messageId;
var validationGroup = attrs.validationGroup;
var messageElem;

if (attrs.messageId) messageElem = angular.element(document.querySelector('#' + attrs.messageId));
if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup)));
else messageElem = element.next();

if (element.attr('no-validation-message')) {
Expand Down Expand Up @@ -413,9 +415,11 @@
var invalidFunc = function(element, validMessage, validation, scope, ctrl, attrs) {
var messageToShow = validMessage || $validationProvider.getDefaultMsg(validation).error;
var invalidCallback = $parse('error');
var messageId = attrs.messageId;
var validationGroup = attrs.validationGroup;
var messageElem;

if (attrs.messageId) messageElem = angular.element(document.querySelector('#' + attrs.messageId));
if (messageId || validationGroup) messageElem = angular.element(document.querySelector('#' + (messageId || validationGroup)));
else messageElem = element.next();

if (element.attr('no-validation-message')) {
Expand All @@ -436,6 +440,33 @@
return false;
};

/**
* Verify whether there is one of the elements inside the group valid.
* If so, it returns true, otherwise, it returns false
*
* @param scope
* @param element
* @param attrs
* @param ctrl
* @return {boolean}
*/
var checkValidationGroup = function(scope, element, attrs, ctrl) {
var validationGroup = attrs.validationGroup;
var validationGroupElems = document.querySelectorAll('*[validation-group=' + validationGroup + ']');
var validationGroupElem;

// Set the element to be invalid
ctrl.$setValidity(ctrl.$name, false);

// Loop through all elements inside the group
for (var i = 0, len = validationGroupElems.length; i < len; i++) {
validationGroupElem = angular.element(validationGroupElems[i]);

// If the element is valid and it's not the same element with the current checking element, returns true
if (validationGroupElem.hasClass('ng-valid') && validationGroupElem[0] !== element[0]) return true;
}
return false;
};

/**
* collect elements for focus
Expand Down Expand Up @@ -465,6 +496,7 @@
var successMessage = validator + 'SuccessMessage';
var errorMessage = validator + 'ErrorMessage';
var expression = $validationProvider.getExpression(validator);
var validationGroup = attrs.validationGroup;
var valid = {
success: function() {
validFunc(element, attrs[successMessage], validator, scope, ctrl, attrs);
Expand All @@ -489,7 +521,11 @@
return $q.all([$validationProvider.getExpression(validator)(value, scope, element, attrs, validatorParam)])
.then(function(data) {
if (data && data.length > 0 && data[0]) return valid.success();
else return valid.error();
else if (validationGroup) {
// Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not.
// If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message.
if (!checkValidationGroup(scope, element, attrs, ctrl)) valid.error();
} else return valid.error();
}, function() {
return valid.error();
});
Expand All @@ -499,11 +535,14 @@
else if (expression.constructor === RegExp) {
// Only apply the test if the value is neither undefined or null
if (value !== undefined && value !== null) return $validationProvider.getExpression(validator).test(value) ? valid.success() : valid.error();
else return valid.error();
else if (validationGroup) {
// Whenever the element is invalid, we'll check whether one of the elements inside the its group valid or not.
// If there is a valid element, its invalid message won't be shown, Otherwise, shows its invalid message.
if (!checkValidationGroup(scope, element, attrs, ctrl)) valid.error();
} else return valid.error();
} else return valid.error();
};


/**
* generate unique guid
*/
Expand All @@ -519,6 +558,15 @@
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
/**
* All attributes
*/
var validator = attrs.validator;
var messageId = attrs.messageId;
var validationGroup = attrs.validationGroup;
var validMethod = attrs.validMethod;
var ngModel = attrs.ngModel;

/**
* watch
* @type {watch}
Expand All @@ -535,7 +583,7 @@
*
* Convert user input String to Array
*/
var validation = attrs.validator.split(',');
var validation = validator.split(',');

/**
* guid use
Expand All @@ -554,7 +602,7 @@
/**
* Default Valid/Invalid Message
*/
if (!attrs.messageId) element.after('<span></span>');
if (!(messageId || validationGroup)) element.after('<span></span>');

/**
* Set custom initial validity
Expand All @@ -579,7 +627,7 @@
ctrl.$setPristine();
ctrl.$setValidity(ctrl.$name, undefined);
ctrl.$render();
if (attrs.messageId) angular.element(document.querySelector('#' + attrs.messageId)).html('');
if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html('');
else element.next().html('');

if ($validationProvider.resetCallback) $validationProvider.resetCallback(element);
Expand All @@ -589,9 +637,7 @@
/**
* Check validator
*/


var validMethod = (angular.isUndefined(attrs.validMethod)) ? $validationProvider.getValidMethod() : attrs.validMethod;
validMethod = (angular.isUndefined(validMethod)) ? $validationProvider.getValidMethod() : validMethod;

/**
* Click submit form, check the validity when submit
Expand All @@ -606,7 +652,7 @@
// clear previous scope.$watch
watch();
watch = scope.$watch(function() {
return scope.$eval(attrs.ngModel);
return scope.$eval(ngModel);
}, function(value, oldValue) {
// don't watch when init
if (value === oldValue) {
Expand Down Expand Up @@ -645,7 +691,7 @@
*/
if (validMethod === 'blur') {
element.bind('blur', function() {
var value = scope.$eval(attrs.ngModel);
var value = scope.$eval(ngModel);
scope.$apply(function() {
checkValidation(scope, element, attrs, ctrl, validation, value);
});
Expand All @@ -666,7 +712,7 @@
* This is the default method
*/
scope.$watch(function() {
return scope.$eval(attrs.ngModel);
return scope.$eval(ngModel);
}, function(value) {
/**
* dirty, pristine, viewValue control here
Expand All @@ -676,7 +722,7 @@
ctrl.$setViewValue(ctrl.$viewValue);
} else if (ctrl.$pristine) {
// Don't validate form when the input is clean(pristine)
if (attrs.messageId) angular.element(document.querySelector('#' + attrs.messageId)).html('');
if (messageId || validationGroup) angular.element(document.querySelector('#' + (messageId || validationGroup))).html('');
else element.next().html('');
return;
}
Expand All @@ -689,7 +735,7 @@
*/
attrs.$observe('noValidationMessage', function(value) {
var el;
if (attrs.messageId) el = angular.element(document.querySelector('#' + attrs.messageId));
if (messageId || validationGroup) el = angular.element(document.querySelector('#' + (messageId || validationGroup)));
else el = element.next();
if (value === 'true' || value === true) el.css('display', 'none');
else if (value === 'false' || value === false) el.css('display', 'block');
Expand Down
Loading

0 comments on commit df9f4d4

Please sign in to comment.