Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them; it only takes a minute:

I have built a directive that generates an input field based on meta ($scope.meta) data. My problem is supporting angular's Form validation.

Plunkr: http://plnkr.co/edit/AfR5cWyywGlCECS6h6Dr?p=preview

Explanation: directive takes a meta parameter which describes what input field type it is and other related information to render the field. All the attributes defined on directive will be copied to element and finally will replace the directive after compilation and linking.

View:

<form name="userForm">
    <!-- Username -->
    <poly-field ng-model="storage" meta="meta" key="username" name="username" ng-minlength="3" ng-maxlength="8"></poly-field>
    <p ng-show="userForm.username.$error.minlength" class="help-block">Username is too short.</p>
    <p ng-show="userForm.username.$error.maxlength" class="help-block">Username too long</p>

    <!-- Email -->
    <poly-field ng-model="storage" meta="meta" key="email" name="email" ng-required></poly-field>
    <p ng-show="userForm.email.$error.minlength" class="help-block">Email is too short.</p>
    <p ng-show="userForm.email.$error.maxlength" class="help-block">Email is too long</p>
</form>

Entering username longer or shorter than allowed should show me an error. Skipping email field show me an error too.

Here is the View source-code after applying the directive:

<form name="userForm" class="ng-pristine ng-valid ng-valid-required">
        <!-- Username -->
        <input type="undefined" ng-model="storage.username" meta="meta" key="username" name="username" ng-minlength="3" ng-maxlength="8" class="ng-scope ng-valid-minlength ng-dirty ng-valid ng-valid-maxlength">
        <p ng-show="userForm.username.$error.minlength" class="help-block ng-hide">Username is too short.</p>
        <p ng-show="userForm.username.$error.maxlength" class="help-block ng-hide">Username too long</p>

        <!-- Email -->
        <input type="undefined" ng-model="storage.email" meta="meta" key="email" name="email" ng-required="" class="ng-scope ng-valid ng-valid-required ng-dirty">
        <p ng-show="userForm.email.$error.minlength" class="help-block ng-hide">Email is too short.</p>
        <p ng-show="userForm.email.$error.maxlength" class="help-block ng-hide">Email is too long</p>
</form>

Here is the model the view and the directive working with, it's declared in ParentCtrl.

$scope.storage = {
    username: "Kitkat",
    email: "[email protected]"
};

That's the meta information, this tell how the input field look like (is it a text? date? etc.)

 $scope.meta = {
    username: { type: "text" },
    email: { type: "text" }
};

Directive declaration:

app.directive('polyField', function($compile) {
    var linkFn = function(scope, element, attributes) {
        // create input element depending on the type
        var template = document.createElement("input");
        template.type = scope.storage[attributes.key].type;

        // copy all attributes
        for (var attr in attributes.$attr) {
            if (attributes.$attr[attr] == 'ng-model') {
                template.setAttribute('ng-model', attributes[attr] + '.' + attributes.key);
            } else {
                template.setAttribute(attributes.$attr[attr], attributes[attr]);
            }
        }

        // template compilation, linking and element replacement
        template = angular.element(template.outerHTML);
        var linkTemplate = $compile(template);
        var domElement = linkTemplate(scope);
        element.replaceWith(domElement);
    }

    var directive = {
        restrict: 'E',
        scope: { 
            meta: '=',
            storage: '=ngModel',
            key: '=',
        },
        link: linkFn
    };

    return directive;
  });

Some thoughts:
Each directive creates an isolated scope, so, inside the directive userForm is unseen, however, the storage (ng-model) that is two-way binded is seen from directive's scope and parent scope, if userForm sees what's inside storage it should be ok, isn't it?

I tried to pass userForm to directive scope:

// directive
scope: {
        meta: '=',
        storage: '=ngModel',
        key: '=',
        userForm: '='
}

// template
<poly-field user-form="userForm" ng-model="storage" meta="meta" key="username" name="username" ng-minlength="3" ng-maxlength="8"></poly-field>

but with no effect.

When I look into the directives scope, into userForm field I see that the input field is actually $dirty: false, $invalid: true, and that after I typed into username/email field,

In the source code it's seems that the validation classes are applied correctly: ng-valid-minlength ng-dirty ng-invalid ng-invalid-maxlength still, no validation errors are shown.

Is there anything I can do with it?

share|improve this question

1 Answer 1

up vote 1 down vote accepted

In order for Angular's form-validation to de able to kick in, ngModel looks for a parent ngForm/form directive during its controller's instantiation phase (before the pre-linking phase).

Since you compile and link the element before adding it to the DOM there is no parent element at that time, so it fails to register.

You just need to first insert the element into the DOM and then compile and link it.

Updated plunkr

share|improve this answer
    
It works! Thanks a lot. – tranq Aug 18 '14 at 19:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.