This repository has been archived by the owner on Nov 24, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(valdrFormGroup): add new directive for valdr validated form groups
- add new valdrFormGroup directive which sets validity state for a group of form items and is responsible for adding and removing validation messages if valdr-message is loaded - change class names used by valdr on form group level (default is now ng-invalid/valid instead of has-error has-success,this can be overridden by changing the properties on the valdrClasses value) closes #11, fixes #44, fixes #48
- Loading branch information
Philipp Denzler
committed
Dec 9, 2014
1 parent
f359fe8
commit 3986bf8
Showing
11 changed files
with
331 additions
and
199 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/** | ||
* This directive adds the validity state to a form group element surrounding valdr validated input fields. | ||
* If valdr-messages is loaded, it also adds the validation messages as last element to the element this this | ||
* directive is applied on. | ||
*/ | ||
var valdrFormGroupDirectiveDefinition = | ||
['valdrClasses', 'valdrConfig', function (valdrClasses, valdrConfig) { | ||
return { | ||
restrict: 'EA', | ||
link: function (scope, element) { | ||
if (valdrConfig.addFormGroupClass) { | ||
element.addClass(valdrClasses.formGroup); | ||
} | ||
}, | ||
controller: ['$scope', '$element', function ($scope, $element) { | ||
|
||
var formItems = [], | ||
messageElements = {}; | ||
|
||
/** | ||
* Checks the state of all valdr validated form items below this element. | ||
* @returns {Object} an object containing the states of all form items in this form group | ||
*/ | ||
var getFormGroupState = function () { | ||
|
||
var formGroupState = { | ||
// true if an item in this form group is currently dirty, touched and invalid | ||
invalidDirtyTouchedGroup: false, | ||
// true if all form items in this group are currently valid | ||
valid: true, | ||
// contains the validity states of all form items in this group | ||
itemStates: [] | ||
}; | ||
|
||
angular.forEach(formItems, function (formItem) { | ||
if (formItem.$touched && formItem.$dirty && formItem.$invalid) { | ||
formGroupState.invalidDirtyTouchedGroup = true; | ||
} | ||
|
||
if (formItem.$invalid) { | ||
formGroupState.valid = false; | ||
} | ||
|
||
var itemState = { | ||
name: formItem.$name, | ||
touched: formItem.$touched, | ||
dirty: formItem.$dirty, | ||
valid: formItem.$valid | ||
}; | ||
|
||
formGroupState.itemStates.push(itemState); | ||
}); | ||
|
||
return formGroupState; | ||
}; | ||
|
||
/** | ||
* Updates the classes on this element and the valdr message elements based on the validity states | ||
* of the items in this form group. | ||
* @param formGroupState the current state of this form group and its items | ||
*/ | ||
var updateClasses = function (formGroupState) { | ||
// form group state | ||
$element.toggleClass(valdrClasses.invalidDirtyTouchedGroup, formGroupState.invalidDirtyTouchedGroup); | ||
$element.toggleClass(valdrClasses.valid, formGroupState.valid); | ||
$element.toggleClass(valdrClasses.invalid, !formGroupState.valid); | ||
|
||
// valdr message states | ||
angular.forEach(formGroupState.itemStates, function (itemState) { | ||
var messageElement = messageElements[itemState.name]; | ||
if (messageElement) { | ||
messageElement.toggleClass(valdrClasses.valid, itemState.valid); | ||
messageElement.toggleClass(valdrClasses.invalid, !itemState.valid); | ||
messageElement.toggleClass(valdrClasses.dirty, itemState.dirty); | ||
messageElement.toggleClass(valdrClasses.pristine, !itemState.dirty); | ||
messageElement.toggleClass(valdrClasses.touched, itemState.touched); | ||
messageElement.toggleClass(valdrClasses.untouched, !itemState.touched); | ||
} | ||
}); | ||
}; | ||
|
||
$scope.$watch(getFormGroupState, updateClasses, true); | ||
|
||
this.addFormItem = function (ngModelController) { | ||
formItems.push(ngModelController); | ||
}; | ||
|
||
this.removeFormItem = function (ngModelController) { | ||
var index = formItems.indexOf(ngModelController); | ||
if (index >= 0) { | ||
formItems.splice(index, 1); | ||
} | ||
}; | ||
|
||
this.addMessageElement = function (ngModelController, messageElement) { | ||
$element.append(messageElement); | ||
messageElements[ngModelController.$name] = messageElement; | ||
}; | ||
|
||
this.removeMessageElement = function (ngModelController) { | ||
messageElements[ngModelController.$name].remove(); | ||
}; | ||
|
||
}] | ||
}; | ||
}]; | ||
|
||
angular.module('valdr') | ||
.directive('valdrFormGroup', valdrFormGroupDirectiveDefinition); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
describe('valdrFormGroup directive', function () { | ||
|
||
// VARIABLES | ||
|
||
var $scope, $compile, element, valdr, valdrClasses, valdrConfig, ngModelController, | ||
personConstraints = { | ||
'Person': { | ||
'firstName': { | ||
'size': { | ||
'min': 0, | ||
'max': 10, | ||
'message': 'size' | ||
} | ||
}, | ||
'lastName': { | ||
'size': { | ||
'min': 0, | ||
'max': 10, | ||
'message': 'size' | ||
} | ||
} | ||
} | ||
}; | ||
|
||
var formGroupTemplate = | ||
'<form valdr-type="Person" valdr-form-group>' + | ||
'<input type="text" name="firstName" valdr-no-message ng-model="person.firstName">' + | ||
'<input type="text" name="lastName" valdr-no-message ng-model="person.lastName">' + | ||
'</form>'; | ||
|
||
// TEST UTILITIES | ||
|
||
function compileTemplate(template) { | ||
element = $compile(angular.element(template))($scope); | ||
$scope.$digest(); | ||
} | ||
|
||
function compileFormGroupTemplate() { | ||
compileTemplate(formGroupTemplate); | ||
ngModelController = element.find('input').controller('ngModel'); | ||
} | ||
|
||
beforeEach(function () { | ||
module('valdr'); | ||
}); | ||
|
||
beforeEach(inject(function ($rootScope, _$compile_, _valdr_, _valdrClasses_, _valdrConfig_) { | ||
$compile = _$compile_; | ||
valdr = _valdr_; | ||
valdrClasses = _valdrClasses_; | ||
valdrConfig = _valdrConfig_; | ||
|
||
$scope = $rootScope.$new(); | ||
$scope.person = { }; | ||
valdr.addConstraints(personConstraints); | ||
})); | ||
|
||
|
||
describe('valdrFormGroup', function () { | ||
|
||
beforeEach(function () { | ||
compileFormGroupTemplate(); | ||
}); | ||
|
||
describe('form-group class', function () { | ||
|
||
it ('should add form group class by default', function () { | ||
expect(element.hasClass(valdrClasses.formGroup)).toBe(true); | ||
}); | ||
|
||
it ('should not add form group class if option is disabled in valdrConfig', function () { | ||
// given | ||
valdrConfig.addFormGroupClass = false; | ||
|
||
// when | ||
compileFormGroupTemplate(); | ||
|
||
// then | ||
expect(element.hasClass(valdrClasses.formGroup)).toBe(false); | ||
}); | ||
|
||
}); | ||
|
||
it('should not set valid and invalidDirtyTouchedGroup classes if all items are valid', function () { | ||
expect(element.hasClass(valdrClasses.valid)).toBe(true); | ||
expect(element.hasClass(valdrClasses.invalid)).toBe(false); | ||
expect(element.hasClass(valdrClasses.invalidDirtyTouchedGroup)).toBe(false); | ||
}); | ||
|
||
it('should not set invalid class if an item is not valid', function () { | ||
// given | ||
$scope.person.firstName = 'This name is too long for the constraints.'; | ||
|
||
// when | ||
$scope.$digest(); | ||
|
||
// then | ||
expect(element.hasClass(valdrClasses.invalid)).toBe(true); | ||
expect(element.hasClass(valdrClasses.valid)).toBe(false); | ||
expect(element.hasClass(valdrClasses.invalidDirtyTouchedGroup)).toBe(false); | ||
}); | ||
|
||
it('should add invalidDirtyTouchedGroup class if an input is dirty, touched and invalid', function () { | ||
// given | ||
$scope.person.firstName = 'This name is too long for the constraints.'; | ||
ngModelController.$invalid = true; | ||
ngModelController.$dirty = true; | ||
ngModelController.$touched = true; | ||
|
||
// when | ||
$scope.$digest(); | ||
|
||
// then | ||
expect(element.hasClass(valdrClasses.invalid)).toBe(true); | ||
expect(element.hasClass(valdrClasses.valid)).toBe(false); | ||
expect(element.hasClass(valdrClasses.invalidDirtyTouchedGroup)).toBe(true); | ||
}); | ||
|
||
it('should be valid if no form items are registered', function () { | ||
// given | ||
var template = '<form valdr-type="Person" valdr-form-group></form>'; | ||
|
||
// when | ||
compileTemplate(template); | ||
|
||
// then | ||
expect(element.hasClass(valdrClasses.valid)).toBe(true); | ||
expect(element.hasClass(valdrClasses.invalid)).toBe(false); | ||
expect(element.hasClass(valdrClasses.invalidDirtyTouchedGroup)).toBe(false); | ||
}); | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.