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(core): add support for <select> elements
closes #20
- Loading branch information
Philipp Denzler
committed
Apr 17, 2014
1 parent
6b51442
commit adbb18c
Showing
8 changed files
with
352 additions
and
262 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
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,62 @@ | ||
/** | ||
* This directive adds validation to all input and select fields which are bound to an ngModel and are surrounded | ||
* by a valdrType directive. | ||
*/ | ||
var valdrFormItemDirectiveDefinition = | ||
['valdrEvents', 'valdr', 'valdrUtil', 'valdrClasses', function (valdrEvents, valdr, valdrUtil, valdrClasses) { | ||
return { | ||
restrict: 'E', | ||
require: ['?^valdrType', '?^ngModel'], | ||
link: function (scope, element, attrs, controllers) { | ||
|
||
var valdrTypeController = controllers[0], | ||
ngModelController = controllers[1], | ||
fieldName = attrs.name, | ||
parentElement = element.parent(); | ||
|
||
// Stop right here if this is a form item that's either not inside of a valdr-type block | ||
// or there is no ng-model bound to it. | ||
if (!valdrTypeController || !ngModelController) { | ||
return; | ||
} | ||
|
||
if (valdrUtil.isEmpty(fieldName)) { | ||
throw new Error('form element is not bound to a field name'); | ||
} | ||
|
||
var updateClassOnParentElement = function(valid) { | ||
parentElement.addClass(valid ? valdrClasses.valid : valdrClasses.invalid); | ||
parentElement.removeClass(valid ? valdrClasses.invalid : valdrClasses.valid); | ||
}; | ||
|
||
var updateNgModelController = function (validationResult) { | ||
ngModelController.$setValidity('valdr', validationResult.valid); | ||
ngModelController.valdrViolations = validationResult.violations; | ||
}; | ||
|
||
var validate = function (value) { | ||
var validationResult = valdr.validate(valdrTypeController.getType(), fieldName, value); | ||
updateNgModelController(validationResult); | ||
updateClassOnParentElement(validationResult.valid); | ||
return validationResult.valid ? value : undefined; | ||
}; | ||
|
||
ngModelController.$parsers.push(validate); | ||
ngModelController.$formatters.push(validate); | ||
|
||
scope.$on(valdrEvents.revalidate, function () { | ||
validate(ngModelController.$viewValue); | ||
}); | ||
|
||
element.bind('blur', function () { | ||
if (ngModelController.$invalid && ngModelController.$dirty) { | ||
parentElement.addClass(valdrClasses.dirtyBlurred); | ||
} | ||
}); | ||
} | ||
}; | ||
}]; | ||
|
||
angular.module('valdr') | ||
.directive('input', valdrFormItemDirectiveDefinition) | ||
.directive('select', valdrFormItemDirectiveDefinition); |
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,216 @@ | ||
describe('valdrFormItem directive', function () { | ||
|
||
// VARIABLES | ||
|
||
var $scope, $compile, element, valdr, valdrEvents, valdrClasses, ngModelController, | ||
violations = ['violationsArray']; | ||
|
||
var inputTemplate = | ||
'<form name="demoForm">' + | ||
'<div valdr-type="TestClass">' + | ||
'<input type="text" name="fieldName" ng-model="myObject.field">' + | ||
'</div>' + | ||
'</form>'; | ||
|
||
var selectTemplate = | ||
'<form name="demoForm">' + | ||
'<div valdr-type="TestClass">' + | ||
'<select name="fieldName" ng-model="myObject.field">' + | ||
'<option value></option>' + | ||
'<option value="1">First</option>' + | ||
'<option value="2">Second</option>' + | ||
'</select>' + | ||
'</div>' + | ||
'</form>'; | ||
|
||
// TEST UTILITIES | ||
|
||
function compileTemplate (template) { | ||
element = $compile(angular.element(template))($scope); | ||
$scope.$digest(); | ||
} | ||
|
||
function compileInputTemplate () { | ||
compileTemplate(inputTemplate); | ||
ngModelController = element.find('input').controller('ngModel'); | ||
} | ||
|
||
// COMMON SETUP | ||
|
||
beforeEach(function () { | ||
module('valdr'); | ||
|
||
/** | ||
* Mock the valdr to always return 'true' when the value equals the string 'valid'. | ||
*/ | ||
module(function ($provide) { | ||
$provide.value('valdr', { | ||
validate: function (typeName, fieldName, value) { | ||
return { | ||
valid: value === 'valid', | ||
violations: violations | ||
}; | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
beforeEach(inject(function ($rootScope, _$compile_, _valdr_, _valdrEvents_, _valdrClasses_) { | ||
$compile = _$compile_; | ||
$scope = $rootScope.$new(); | ||
$scope.myObject = { field: 'fieldValue' }; | ||
valdr = _valdr_; | ||
valdrEvents = _valdrEvents_; | ||
valdrClasses = _valdrClasses_; | ||
})); | ||
|
||
// COMMON TESTS | ||
|
||
function runFormItemCommonTests () { | ||
it('should set the validity to false on ngModelController if validation fails', function () { | ||
// when | ||
$scope.$apply(function () { | ||
$scope.myObject.field = 'invalid'; | ||
}); | ||
|
||
// then | ||
expect(ngModelController.$valid).toBe(false); | ||
expect(ngModelController.valdrViolations).toBe(violations); | ||
}); | ||
|
||
it('should set the validity to true on ngModelController if validation is ok', function () { | ||
// when | ||
$scope.$apply(function () { | ||
$scope.myObject.field = 'valid'; | ||
}); | ||
|
||
// then | ||
expect(ngModelController.$valid).toBe(true); | ||
expect(ngModelController.valdrViolations).toBe(violations); | ||
}); | ||
|
||
it('should add class to surrounding element if data is valid', function () { | ||
// given | ||
var surroundingElement = element.find('div'); | ||
|
||
// when | ||
$scope.$apply(function () { | ||
$scope.myObject.field = 'valid'; | ||
}); | ||
|
||
// then | ||
expect(surroundingElement.hasClass(valdrClasses.valid)).toBe(true); | ||
expect(surroundingElement.hasClass(valdrClasses.invalid)).toBe(false); | ||
}); | ||
|
||
it('should add class to surrounding element if data is invalid', function () { | ||
// given | ||
var surroundingElement = element.find('div'); | ||
|
||
// when | ||
$scope.$apply(function () { | ||
$scope.myObject.field = 'invalid'; | ||
}); | ||
|
||
// then | ||
expect(surroundingElement.hasClass(valdrClasses.invalid)).toBe(true); | ||
expect(surroundingElement.hasClass(valdrClasses.valid)).toBe(false); | ||
}); | ||
|
||
|
||
it('should handle constraint changed events', function () { | ||
// given | ||
spyOn(valdr, 'validate').andCallThrough(); | ||
|
||
// when | ||
$scope.$broadcast(valdrEvents.revalidate); | ||
|
||
// then | ||
expect(valdr.validate).toHaveBeenCalled(); | ||
}); | ||
} | ||
|
||
describe('on input fields', function () { | ||
|
||
beforeEach(function (){ | ||
compileInputTemplate(); | ||
}); | ||
|
||
runFormItemCommonTests(); | ||
|
||
it('should throw error if no field name is provided on the input', function () { | ||
// given | ||
var invalidInput = | ||
'<form name="demoForm"><div valdr-type="TestClass">' + | ||
'<input type="text" ng-model="myObject.field">' + | ||
'</div></form>'; | ||
|
||
// when / then | ||
expect(function () { | ||
$compile(angular.element(invalidInput))($scope); | ||
}).toThrow(new Error('form element is not bound to a field name')); | ||
}); | ||
}); | ||
|
||
describe('on select elements', function () { | ||
|
||
beforeEach(function (){ | ||
compileTemplate(selectTemplate); | ||
ngModelController = element.find('select').controller('ngModel'); | ||
}); | ||
|
||
runFormItemCommonTests(); | ||
|
||
}); | ||
|
||
describe('blur behavior', function () { | ||
var input, surroundingElement; | ||
|
||
beforeEach(function () { | ||
compileInputTemplate(); | ||
input = element.find('input'); | ||
surroundingElement = element.find('div'); | ||
}); | ||
|
||
it('should add dirtyBlurred class when the model is dirty, invalid and the input gets blurred', function () { | ||
// given | ||
ngModelController.$dirty = true; | ||
ngModelController.$invalid = true; | ||
ngModelController.$valid = false; | ||
|
||
// when | ||
input.triggerHandler('blur'); | ||
|
||
// then | ||
expect(surroundingElement.hasClass(valdrClasses.dirtyBlurred)).toBe(true); | ||
}); | ||
|
||
it('should add not dirtyBlurred class when the model is pristine, invalid and the input gets blurred', function () { | ||
// given | ||
ngModelController.$dirty = false; | ||
ngModelController.$invalid = true; | ||
ngModelController.$valid = false; | ||
|
||
// when | ||
input.triggerHandler('blur'); | ||
|
||
// then | ||
expect(surroundingElement.hasClass(valdrClasses.dirtyBlurred)).toBe(false); | ||
}); | ||
|
||
it('should add not dirtyBlurred class when the model is dirty, valid and the input gets blurred', function () { | ||
// given | ||
ngModelController.$dirty = true; | ||
ngModelController.$invalid = false; | ||
ngModelController.$valid = true; | ||
|
||
// when | ||
input.triggerHandler('blur'); | ||
|
||
// then | ||
expect(surroundingElement.hasClass(valdrClasses.dirtyBlurred)).toBe(false); | ||
}); | ||
|
||
}); | ||
|
||
}); |
Oops, something went wrong.