trubliphone
trubliphone

Reputation: 4504

angularjs directive for custom validator with error messages

I want to be able to pass a dictionary of custom validators along with possible error messages to an angular directive such that the appropriate error messages are rendered whenever the corresponding validator returns false.

Here is some code:

base.js:

function not_bob(value) {
  return value != "bob";
}

function not_alice(value) {
  return value != "alice";
}

app.js:

(function() {
    var app = angular.module("my_app");
    app.controller("MyController", [ "$scope", function($scope) {
        var my_controller = this;
        my_controller.person = {
          "first_name": "fred",
          "last_name": "jones"
        }
        my_controller.validators = {
            "first_name" : [
                {
                    "name": "validatebob",
                    "fn": not_bob,
                    "msg": "bob is not allowed"
                },
                {
                    "name": "validatealice",
                    "fn": not_alice,
                    "msg": "alice is not allowed"
                }
            ]
        };

    }]);
    app.directive('myvalidators', ['$compile', function($compile) {
        return {
            require: ['^form', 'ngModel'],
            scope: {
                validators: '=myvalidators'
            },
            link: function(scope, elm, attrs, ctrls) {
                var form = ctrls[0];
                var ctrl = ctrls[1];
                var parent = elm.parent();
                $.each(scope.validators, function(i, validator) {
                    ctrl.$validators[validator.name] = function(modelValue, viewValue) {
                        return validator.fn(viewValue);
                    };
                    var error_class = [form.$name, ctrl.$name, "$error", validator.name].join(".");
                    var error_content = "<div ng-show='" + error_class + "'>" + validator.msg + "</div>"
                    var compiled_error_content = $compile(error_content)(scope)
                    parent.append(compiled_error_content);
                });
            }
        };
    }]);
})();

template.html:

  <html>
      <script type="text/javascript" src="base.js"></script>
      <script type="text/javascript" src="app.js"></script>
      <div ng-app="my_app">
        <div ng-controller="MyController as my_controller">
          <form name="person_form" novalidate>
            <input type="text" class="form-control" name="first_name" id="id_first_name" ng-model="my_controller.person.first_name" myvalidators="my_controller.validators['first_name']">
          </form>
        </div> 
      </div>
    </html>

This looks complicated (and it's a contrived example), but it's mostly working; I pass an array as the argument to the directive. Each element of the array is a JSON object w/ a "name" telling me what the validator is called, a "fn" telling me what function to run to perform validation, and a "msg" telling me what to display if that function returns false.

Sure enough, the directive causes the input to be invalid when I type "bob" or "alice", and it adds the following sibling elements:

<div ng-show="person_form.first_name.$error.validatebob">bob is not allowed</div>
<div ng-show="person_form.first_name.$error.validatealice">alice is not allowed</div>

However, those elements never show up.

Interestingly, if I just manually add those elements to the template (ie: not using the directive) then the correct message appears when the corresponding validator returns false.

Any ideas on what I'm doing wrong?

Thanks


edit: I'm pretty sure it's something to do w/ scope; Perhaps the "person_form" object in the controller isn't available in the directive?

Upvotes: 0

Views: 155

Answers (1)

trubliphone
trubliphone

Reputation: 4504

Aha! As suspected, the issue was w/ scope.

The form was in the scope of my controller and not the isolated scope of my directive. Changing the line in the directive from this:

var compiled_error_content = $compile(error_content)(scope)

to this:

var compiled_error_content = $compile(error_content)(scope.$parent)

Did the trick.

Upvotes: 0

Related Questions