Search code examples
angularjsvalidationangularjs-directiveangularjs-scopeng-maxlength

Unable to get $error.maxlength validation in Directive


I am creating a directive that adds a template with text type input to the view. In this directive, I am trying to add the span class for error notification if the text field input is more than max length setting provided. I have a code something like this:

<div ng-app="app">
<form name="userForm" ng-submit="processForm()" novalidate>
    <div use-text-box name="xyz" ng-maxlength="10" required> </div>

    <button type="submit" class="btn btn-success">Submit</button>
</form>
</div>   

My Directive like this:

var app = angular.module('app', []);

app.directive('useTextBox', function() {

              return {
                  replace:true,
                  compile: function(element,attrs) {

                        var name =  attrs.name;

                        var maxLengthError = attrs.hasOwnProperty('ngMaxlength') ? '<span ng-show="userForm.' + attrs.name + '.$error.maxlength" class="help-block">Text is too long. The limit is ' + attrs.ngMaxlength + ' characters.</span>' : '';

                        var htmlText = '<input type="text" name="' + name + '" ng-maxlength="' + attrs.ngMaxlength + '" required />' + 
                                            maxLengthError;

                        element.replaceWith(htmlText);

                }
              };
});

But in the above code, the directive is generating the input text field etc.. without a problem. However, it is not showing the error message if the max length is more than 10. What am I doing wrong?

Here is the link to the jsfiddle for the above example: http://jsfiddle.net/fB45J/3/


Solution

  • I don't know if you're just learning and trying to understand directives, but you don't even need a directive to accomplish what you want.

    Here's a Fiddle without the directive: http://jsfiddle.net/mikeeconroy/fB45J/7/

    <div ng-app="app">
        <ng-form name="userForm" novalidate role="form" ng-controller="myFormCtrl">
            <div>
                <p class="text-muted">Enter in some text then remove it.  You need to make it such that Angular sees the form as "dirty" and then tries validate the form.</p><br>
            </div>
            <div>
                <input type="text" name="xyz" ng-model="xyz" maxlength="10" required>
                <span ng-show="userForm.xyz.$dirty && userForm.xyz.$error.required && userForm.xyz.$invalid" style="color: #900;">This field is required!</span>
            </div>
    
            <br />   <br />  
            <button type="button" ng-click="processForm()" class="btn btn-success">Submit</button>
        </ng-form>
    </div>
    

    Here's the Angular. Its not doing anything much just showing how '$dirty' on a form element works.

    var app = angular.module('app', []);
    app.controller('myFormCtrl',function($scope,$element){
        $scope.form = $element.controller('form');
        $scope.processForm = function(){
            // set form to dirty
            $scope.form.xyz.$dirty = true;
            console.log('Processing!');
        };
    });
    

    EDIT: Here's the fix using your directive approach

    http://jsfiddle.net/mikeeconroy/fB45J/8/

    app.directive('useTextBox', function($compile,$timeout) {
    
              return {
                  replace:true,
                  scope: false,
                  link: function(scope,element,attrs) {
    
                        var name =  attrs.name;
    
                        var maxLengthError = attrs.hasOwnProperty('ngMaxlength') ? '<span ng-show="userForm.' + attrs.name + '.$error.maxlength" class="help-block">The limit is ' + attrs.ngMaxlength + ' characters.</span>' : '';
    
                      var htmlText = '<div><input type="text" id="' + name + '" name="' + name + '" ng-maxlength="' + attrs.ngMaxlength + '" ng-model="test_' + attrs.name + '" required>' +   maxLengthError + '</div>';
    
                      $compile(htmlText)(scope,function(_element,_scope){
                        element.replaceWith(_element);
                      });
    
                } // end link
              };
    });
    

    You need to inject $compile into your directive and then use it to compile your HTML and insert it with the correct scope. _element will be the compiled new element.

    $compile(htmlText)(scope,function(_element,_scope){
        element.replaceWith(_element);
    });
    

    EDIT: Here's another example using just the compile property of a directive

    http://jsfiddle.net/mikeeconroy/dzP9L/1/

    I guess it seems that the difference between your example and this one is the introduction of the form's controller.