2

I'm trying to write a custom validator require-items that will yield valid/invalid for the input form field based on the length of an array in the scope...in this case skillTags

<input
    type="text"
    name="tags"
    ng-model="newTag"
    class="form-control"
    placeholder="Enter tags: (eg. JavaScript, HTML5)"
    ng-keyup="search($event)"
    ng-focus="search($event)"
    ng-class="{ 'has-results': matches.length }"
    require-items="{{skillTags.length}}"
    mongoose-error>



//custom validator not working
'use strict';

angular.module('offsiteApp')
    .directive('requireItems', function (){
        return {
            require: 'ngModel',
            link: function(scope, elem, attr, ngModel) {
                var len = parseInt(attr.requireItems);

                //For DOM -> model validation
                ngModel.$parsers.unshift(function(value) {
                    var valid = len ? true : false;

                    ngModel.$setValidity('require-items', valid);
                    return valid ? value : undefined;
                });

                //For model -> DOM validation
                ngModel.$formatters.unshift(function(value) {
                    var valid = len ? true : false;

                    ngModel.$setValidity('require-items', valid);
                    return value;
                });
            }
        };
    });



<p class="help"
   ng-show="form.tags.$error['require-items'] && submitted">
    Skill tags are required.
</p>
1
  • can you provide a jsfiddle or plunkr, as to confirm you are actually putting the input element in a form element Commented Feb 15, 2015 at 22:36

1 Answer 1

4

I've never seen $formatters and $parsers being used for validations. They are meant, as far as I know, only to be used for exactly what they are named.

Typically, to validate, you need to hook into $validators - this will run anytime either a model or a view value change.

But in your example, your validation doesn't even depend on the input - so I wonder why you would even use validation here.

In any case, since you don't care about the input, you just need to observe changes to the attribute value and $setValidity accordingly:

.directive("requireItems", function(){
  return {
    require: "?ngModel",
    link: function(scope, element, attrs, ngModel){
      if (!ngModel) return;

      attrs.$observe("requireItems", function(){
        var val = parseInt(attrs.requireItems);
        ngModel.$setValidity("require-items", !!val);
      });
    }
  }
});
Sign up to request clarification or add additional context in comments.

3 Comments

The input is needed to collect info (in this case tags) from the user. The value of the input is not what gets sent directly to the backend. A tag object is either created (or fetched for existing tags) and then stored in an array on $scope.skillTags. However in order for the form to be valid, there must be at least one tag object in this array. This is what I'm trying to validate the dummy input against.
By the way, this works. Can you explain what its doing? mainly ngModel never seen this before. It appears to observe the <input require-items> value that is passed and sets validity based on that value being present (this is what I was after).
Glad it works. I didn't understand what to explain exactly. attrs has the method $observe that works similarly to $scope.$watch, just for the attribute value.

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.