Reputation: 33199
I have some basic form/input html that works (including validation) if explicitly written as follows:
<form name="forms.create" novalidate>
<div class="si-container">
<div class="si-input-container">
<input class="si-input" name="someNum" placeholder="Enter a number" ng-model="formdata.number" type="number" min="40"/>
</div>
<div class="si-error">
<div ng-show="forms.create.someNum.$error.min">Error! Value must be > 40.</div>
</div>
</div>
</form>
Now what I want to do is create a directive that allows me to write the html below, but result in the html above:
<form name="forms.create" novalidate>
<div special-input name="someNum" placeholder="Enter a number" type="number" ng-model="formdata.number">
<div error-type="min" error-value="40">Error! Value must be > 40.</div>
</div>
</form>
My attempt at the special-input
directive (simplified) is as follows:
.directive('specialInput', [function(){
return {
compile: function(elem, attrs){
var input = angular.element('<input class="si-input"/>');
input.attr('placeholder', attrs.placeholder);
input.attr('type', attrs.type);
input.attr('name', attrs.name);
input.attr('ng-model', attrs.ngModel);
var errorCont = angular.element('<div class="si-error"></div>');
var errors = elem.children();
angular.forEach(errors, function(error){
var err = angular.element(error);
var type = err.attr('error-type');
var value = err.attr('error-value');
input.attr(type, value);
var formName = elem.parent().attr('name');
errorCont.append('<div ng-show="' + formName + '.' + attrs.name + '.$error.' + type + '">' + err.html() + '</div>');
});
var cont = angular.element('<div class="si-container"></div>');
cont.append('<div class="si-floating-label">' + attrs.placeholder + '</div>');
cont.append('<div class="si-input-container">' + input[0].outerHTML + '</div>');
cont.append('<div class="si-underline"></div>');
cont.append(errorCont);
elem.replaceWith(cont[0].outerHTML);
}
};
}]);
Now the resultant html using the directive above looks about right. If I put {{formdata.number}}
below the form the value changes as expected. The problem is that now the validation never shows.
For example, if I put the value 5
in the input and inspect the form object, I get weird results. $dirty
is set to true for form
, but not for form.someNum
. If I put 55
in the input, $dirty
is still set to false for form.someNum
, but $modelValue
and $viewValue
both show 55.
Any ideas or suggestions? Here is a fiddle to help with any testing. If you put 50 in the input box you should see the value below, but put 5 and the error does not appear
UPDATE
I have managed to get it working by moving the dom changes into the link function instead of the compile function, and adding this:
elem.replaceWith(cont);
$compile(cont)(scope);
I am still puzzled though, as to why this works, while altering the dom in the exact same way in the compile function doesn't work. Is anyone able to explain this?
Upvotes: 0
Views: 96
Reputation: 11547
It's because the original ng-model
is still get compiled even the original DOM has already been replaced by the new one in your compile function.
The ng-model
directive will register itself to a parent form in its postLink function. Due to the fact that the postLink function will be executed in reverse (child's before parent's), the new ng-model
will do the registration first, thus it will be overridden by the one from the original ng-model
eventually.
To avoid this problem, you could change the original ng-model
to another name such as my-model
, then rename it to ng-model
later in your compile function.
Example jsfiddle: http://jsfiddle.net/Wr3cJ/1/
Hope this helps.
Upvotes: 1