Adding bootstrap validation styling to angular with a custom directive
// Directive for applying bootstrap validation styles. | |
/* | |
Sample HTML block. | |
<div class="form-group has-feedback" bs-style> | |
<label for="fName">First Name</label> | |
<input type="text" class="form-control" name="fName" id="fName" ng-model="vm.fName" | |
ng-minlength="2" ng-maxlength="20" required/> | |
<span class="form-control-feedback glyphicon"></span> | |
<span class="help-block ng-hide"></span> | |
</div> | |
Sample HTML block with "short" option. | |
<div class="form-group has-feedback" bs-style="short"> | |
<label for="fName">First Name</label> | |
<input type="text" class="form-control" name="fName" id="fName" ng-model="vm.fName" | |
ng-minlength="2" ng-maxlength="20" required/> | |
<span class="form-control-feedback glyphicon"></span> | |
<span class="help-block ng-hide"></span> | |
</div> | |
*/ | |
'use strict'; | |
angular | |
.module('app') | |
.directive('bsStyle', function() { | |
return { | |
restrict: 'A', | |
link: function(scope, element, attrs) { | |
// Find the input in this form-group. | |
for (var el of ['input', 'select', 'textarea']) { | |
var input = element.find(el); | |
if (!input.length) continue; | |
// Get the ngModelController for this input. | |
var ngm = input.controller('ngModel'); | |
// Add a $watchGroup to handle the bootstrap validation styling. | |
// Monitor the $valid, $dirty and $touched, and $error values | |
// independently, so the listener fires when any of them change. | |
scope.$watchGroup([ | |
function isValid() { return ngm.$valid; }, | |
function isTD() { return ngm.$touched && ngm.$dirty; }, | |
function hasError() { return Object.keys(ngm.$error).join(); } | |
], | |
function updateStyles(newValues, oldValues) { | |
// This should only happen in the initialization phase. Ignore it. | |
if (angular.equals(newValues, oldValues)) return; | |
// Don't do anything unless the input is $touched and $dirty. | |
if (!newValues[1]) return; | |
// Toggle the bootstrap 'has-success' and 'has-error' classes | |
// on the form-group based on $valid. | |
element.toggleClass('has-success', newValues[0]) | |
.toggleClass('has-error', !newValues[0]); | |
// Update the icons and error messages based on $valid and $error. | |
var spans = element.find('span'); | |
for (var i = 0; i < spans.length; i++) { | |
var s = spans.eq(i); | |
if (s.hasClass('form-control-feedback')) { | |
s.toggleClass('glyphicon-ok', newValues[0]) | |
.toggleClass('glyphicon-remove', !newValues[0]); | |
} | |
if (s.hasClass('help-block')) { | |
// Only update the error message when not $valid. | |
if (!newValues[0]) { | |
var label = element.find('label').text(), | |
short = attrs.bsStyle === 'short', | |
e = ngm.$error; | |
// Create an error message based on the triggered error, | |
// the 'short' attribute, and the form-group's label. | |
// Customize these based on the validations used. | |
if (short) { | |
if (e.required) s.text('Required.'); | |
else if (e.minlength) s.text('Too short.'); | |
else if (e.maxlength) s.text('Too long.'); | |
else if (e.mask) s.text('Not Valid.'); | |
} | |
else { | |
if (e.required) | |
s.text(label + ' is required.'); | |
else if (e.minlength) | |
s.text(label + ' must be at least ' + | |
input.attr('ng-minlength') + ' chars.'); | |
else if (e.maxlength) | |
s.text(label + ' must be less than ' + | |
input.attr('ng-maxlength') + ' chars.'); | |
else if (e.mask) | |
s.text(label + ' is not valid.'); | |
} | |
} | |
// Display / hide the message based on $valid. | |
s.toggleClass('ng-hide', newValues[0]); | |
} | |
} | |
} | |
); | |
// Stop any extra loop processing. | |
break; | |
} | |
} | |
}; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment