2

I decided to write a custom directive to help me validate my input boxes. The idea is that I add my new fancy nx-validate directive to a bootstrap div.form-group and it'll check whether my <input/> is $dirty or $invalid and apply the .has-success or .has-error class as required.

For some odd reason, my directive works perfectly under normal circumstances, but the added ng-class is completely ignored inside a ui-bootstrap modal.

Identical code in both the modal and the form

<form name="mainForm">
  <div class="row">
      <div nx-validate class="form-group has-feedback col-lg-6 col-md-6 col-xs-12">
          <label class="control-label">Green if long enough, red if not</label>
          <input type="text" id="name" class="form-control" ng-model="property.name" required="required" ng-minlength="5"/>
          (once touched I do change colour - happy face)
      </div>
  </div>        

And my lovely directive

nitro.directive("nxValidate", function($compile) {
    return {
        restrict: 'A',
        priority: 2000,
        compile: function(element) {

            var node = element;
            while (node && node[0].tagName != 'FORM') {
                console.log (node[0].tagName)
                node = node.parent();
            }
            if (!node) console.error("No form node as parent");
            var formName = node.attr("name");
            if (!formName) console.error("Form needs a name attribute");


            var label = element.find("label");
            var input = element.find("input");
            var inputId = input.attr("id")

            if (!label.attr("for")) {
                label.attr("for", inputId);
            }

            if (!input.attr("name")) {
                input.attr("name", inputId);
            }

            if (!input.attr("placeholder")) {
                input.attr("placeholder", label.html());
            }

            element.attr("ng-class", "{'has-error' : " + formName + "." + inputId + ".$invalid && " + formName + "." + inputId + ".$touched, 'has-success' : " + formName + "." + inputId + ".$valid && " + formName + "." + inputId + ".$touched}");
            element.removeAttr("nx-validate");

            var fn = $compile(element);

            return function($scope) {
                fn($scope);
            }

        }
    }
});

Check it out on plunker: http://plnkr.co/edit/AjvNi5e6hmXcTgpXgTlH?

1
  • Put your button inside the form. Works for me when I do it in the plunker example. Commented Jun 25, 2015 at 22:37

1 Answer 1

3

The simplest way I'd suggest you is you can put those classes by using watch on those fields, this watcher will lie inside the postlink function after compiling a DOM

return function($scope, element) {
    fn($scope);

    $scope.$watch(function(){
      return $scope.modalForm.name.$invalid && $scope.modalForm.name.$touched;
    }, function(newVal){
      if(newVal)
        element.addClass('has-error');
      else
        element.removeClass('has-error');
    })

    $scope.$watch(function(){
      return $scope.modalForm.name.$valid && $scope.modalForm.name.$touched;
    }, function(newVal){
      if(newVal)
        element.addClass('has-success');
      else
        element.removeClass('has-success');
    })
}

Demo Here

Update

The actual better way of doing this would be instead of compiling element from compile, we need $compile the element from the link function itself. The reason behind the compiling DOM in link fn using $compile is that our ng-class attribute does contain the scope variable which is like myForm.name.$invalid ,so when we $compile the DOM of compile function then they are not evaluating value of myForm.name.$invalid variable because compile don't have access to scope & the would be always undefined or blank. So while compile DOM inside the link would have all the scope values are available that does contain myForm.name.$invalid so after compiling it with directive scope you will get your ng-class directive binding will work.

Code

compile: function(element) {
    //..other code will be as is..

    element.removeAttr("nx-validate");
      //var fn = $compile(element); //remove this line from compile fn
      return function($scope, element) {
         //fn($scope);
         $compile(element)($scope); //added in postLink to compile dom to get binding working
      }
 }

Updated Plunkr

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

5 Comments

Thanks for your answer. Could you please explain to me why my example doesn't work by default. Apart from wanting to solve the problem - which I'm confident your answer will, I'd like to understand why I'm having the problem in the first place, and be able to avoid it happening in the future. Is it because the modal content is dynamic, and my directive has the wrong priority or is being compiled at the wrong time, or do you think it's a scoping issue?
@Spider sorry for late response..do look at my updated answer.should work now..I think it was compiling issue because of ng-class
@Spider did you looked at answer?
that was fantastic. Thank you. Can you please point me to a resource where I can study the nuts and bolts so I can understand this properly?
@Spider no there is no any official documents to read this..I'd prefer you to read SO post that would help you to more clear your concept about the same..

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.