Thank you to @farincz for a great answer. Here are some modifications I have made to fit with my use case.
This version provides three directives:
bs-has-success
bs-has-error
bs-has
(a convenience for when you want to use the other two together)
Modifications I have made:
- Added a check to only show the has states when the form field is dirty, i.e. they won't be shown until somebody interacts with them.
- Altered the string passed into
element.find()
for those not using jQuery, as element.find()
in Angular's jQLite only supports finding elements by tagname.
- Added support for select boxes and textareas.
- Wrapped the
element.find()
in a $timeout
to support cases where the element may not yet have it's children rendered to the DOM (e.g. if a child of the element is marked with ng-if
).
- Changed
if
expression to check for the length of the returned array (if(input)
from @farincz's answer always returns true, as the return from element.find()
is a jQuery array).
I hope somebody finds this useful!
angular.module('bs-has', [])
.factory('bsProcessValidator', function($timeout) {
return function(scope, element, ngClass, bsClass) {
$timeout(function() {
var input = element.find('input');
if(!input.length) { input = element.find('select'); }
if(!input.length) { input = element.find('textarea'); }
if (input.length) {
scope.$watch(function() {
return input.hasClass(ngClass) && input.hasClass('ng-dirty');
}, function(isValid) {
element.toggleClass(bsClass, isValid);
});
}
});
};
})
.directive('bsHasSuccess', function(bsProcessValidator) {
return {
restrict: 'A',
link: function(scope, element) {
bsProcessValidator(scope, element, 'ng-valid', 'has-success');
}
};
})
.directive('bsHasError', function(bsProcessValidator) {
return {
restrict: 'A',
link: function(scope, element) {
bsProcessValidator(scope, element, 'ng-invalid', 'has-error');
}
};
})
.directive('bsHas', function(bsProcessValidator) {
return {
restrict: 'A',
link: function(scope, element) {
bsProcessValidator(scope, element, 'ng-valid', 'has-success');
bsProcessValidator(scope, element, 'ng-invalid', 'has-error');
}
};
});
Usage:
<!-- Will show success and error states when form field is dirty -->
<div class="form-control" bs-has>
<label for="text"></label>
<input
type="text"
id="text"
name="text"
ng-model="data.text"
required>
</div>
<!-- Will show success state when select box is anything but the first (placeholder) option -->
<div class="form-control" bs-has-success>
<label for="select"></label>
<select
id="select"
name="select"
ng-model="data.select"
ng-options="option.name for option in data.selectOptions"
required>
<option value="">-- Make a Choice --</option>
</select>
</div>
<!-- Will show error state when textarea is dirty and empty -->
<div class="form-control" bs-has-error>
<label for="textarea"></label>
<textarea
id="textarea"
name="textarea"
ng-model="data.textarea"
required></textarea>
</div>
You can also install Guilherme's bower package that bundles all this together.