Long story short, the idea is to simplify templating by not having to manually add ng-class={'has-error': 'formName.inputName.$invalid'}
on every single form-group
So I want to create a directive that will generate a string that will be added to a template element. That string is an ng-class
attribute with an expression
I thought that creating a quick directive that adds the ng-class attribute during the compile phase would be enough, but it doesn't seem to cut it.
{
restrict: 'C',
compile: function(tElement, tAttrs) {
var $elem = angular.element(tElement),
formName = $elem.parents('[ng-form]').length ? $elem.parents('[ng-form]').attr('ng-form') : $elem.parents('form').attr('name'),
controlName = $elem.find('.form-control').attr('name');
$elem.attr('ng-class', '{"has-error": ' + formName + '.' + controlName + '.$invalid}');
console.log('added ng-class attr', $elem.attr('ng-class'));
}
}
See the console
The 1st
form-group
has itsng-class
attribute added dynamically.
The expression associated with it is correct
But theng-class
directive is never executedThe 2nd
form-group
has itsng-class
attribute added manually in the template and works as expected, obvisouly
--
What am I missing here?
Managed to make it work without adding extra watchers (granted, ng-class uses a watcher, but after testing the proposed solution I saw an increase in watchers amount)
I had to $compile the new DOM element in postLink, but to avoid recursion I need to remove the directive itself. This prevents me to reuse a class-name such as "form-group"
Here's the working Directive
function bsFormClass($compile) {
return {
restrict: 'A',
compile: function (tElement) {
var $elem = angular.element(tElement),
controlName = $elem.find('.form-control').attr('name');
if(!controlName) { return; }
var formName = $elem.parents('[ng-form]').length ? $elem.parents('[ng-form]').attr('ng-form') : $elem.parents('form').attr('name');
$elem.attr('ng-class', '{"has-error": ' + formName + '.' + controlName + '.$invalid}');
$elem.removeAttr('bs-form-class');
return {
post: function(scope, elem, attrs) {
$compile(elem)(scope);
}
}
}
}
}
Thanks for you help all
You cannot add directives to the current element!
In your case the solution is easy though; just watch the appropriate expressions and add the class as necessary:
function formGroupClass () {
return {
restrict: 'C',
link: function(scope, elem, attrs) {
var formName = elem.parents('[ng-form]').length ? elem.parents('[ng-form]').attr('ng-form') : elem.parents('form').attr('name'),
controlName = elem.find('.form-control').attr('name');
scope.$watch(formName + '.' + controlName + '.$invalid', function(newval) {
elem.toggleClass('has-error', !!newval);
});
}
};
}
The ng-class
would add a watch anyway... See forked plunk: http://plnkr.co/edit/oKa6CKFoF1T5WoDzIPkI?p=preview
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With