0

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?

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.