23

I'm hoping this isn't a duplicate - plenty of similar questions about but I can't find an answer that works.

I have an Angular directive, thus:

app.directive('emailInput', function(){
    return {
        restrict: 'E',
        templateUrl: 'template.html',
        link: function(scope, elem, attrs, ctrl){
            elem.bind('keyup', function(){
                // TODO - what?
            })
        }
    }
}

and in the template html:

<input type="email" required ng-model="emailAddress" />

Without knowing the name of the form, inside the link function, I want to know the value of the emailAddress.$valid property - how can I get this?

Greg Smith
  • 2,449
  • 2
  • 24
  • 37
  • 1
    Also see [here](http://stackoverflow.com/a/41072188/1021943) for a newer (and arguably cleaner) solution with AngularJS >=1.5.0. – Priidu Neemre Dec 10 '16 at 16:00

6 Answers6

33

You can require the formController which would give you access to all of the inputs registered to the form

app.directive('emailInput', function(){
  return {
      require: '^form', // We look for it on a parent, since it will be defined somewhere higher on the DOM.
      restrict: 'E',
      templateUrl: 'template.html',
      link: function(scope, elem, attrs, ctrl){
          elem.bind('keyup', function(){
              ctrl.emailAddress.$valid //check validity
          })
      }
  }
}

Remember that Angular keeps track of input elements by name. So you have to give your input a name attribute

<input type="email" required ng-model="emailAddress" name="emailAddress" />

I would also recommend possibly just passing all of this through a directive attribute. You probably don't want to hard code the field names. So you could just have an attribute that takes the validity

inputIsValid='formName.emailAddress.$valid'

And evaluate (or $watch it) in your directive.

Adam
  • 1,143
  • 7
  • 7
  • Can we get access to the field with name like this: name='some.thing'? – Rroman Mar 05 '14 at 12:37
  • Haven't tried it myself, but js does let you use any string as a key on an object. Only difference is you will have to use square bracket operators to access it. ctrl['some.thing'].$valid – Adam Mar 05 '14 at 15:47
  • @Adam how would one go about implementing your second suggestion (inputIsValid)? Could we just 'watch' the string we're passing in? – Soroush Hakami Apr 24 '14 at 14:23
2

We can check validity more easily without knowing the name of input elements.

app.directive('emailInput', function(){
  return {
      require: '^form', // We look for it on a parent, since it will be defined somewhere higher on the DOM.
      restrict: 'E',
      templateUrl: 'template.html',
      link: function(scope, elem, attrs, ctrl){
          elem.bind('keyup', function(){
              ctrl.$valid //check validity here
          })
      }
  }
}
Upalr
  • 2,140
  • 2
  • 23
  • 33
1

This is an old post but for the people who get here by googling, this is the cleanest way to check validity of an input in your directive without knowing its name, so you can use your directive on any input element.

You just need to require the ngModelcontroller:

app.directive('emailInput', function(){
  return {
    require: 'ngModel'
    restrict: 'E',
    templateUrl: 'template.html',
    link: function(scope, elem, attrs, ngModelCtrl){
      elem.bind('keyup', function(){
          ngModelCtrl.$valid //check validity
      })
    }
  }
}

See the AngularJS document for ngModel.NgModelController, $valid under the Properties section:

https://docs.angularjs.org/api/ng/type/ngModel.NgModelController

Sam
  • 93
  • 1
  • 9
  • Your answer shows a code that is *almost* a copy/paste of @preservant above you. Either explain why yours is better or just edit @preservant's answer. You write `require: 'ngModel'` instead of `require: '^form'`. The rest is basically the same. – Ofer Zelig Aug 16 '16 at 04:34
  • @OferZelig The difference is with `require: '^form'` it has to go look for `form` on the element's parents but with `require: 'ngModel'` it only looks on the element itself. Plus, `ngModel` is really the thing we need here not the `form` – Sam Aug 17 '16 at 10:13
  • Yeah so I think best is to edit the previous answer and having a canonical one, it's less confusing for people. – Ofer Zelig Aug 18 '16 at 01:32
1

Know it's an old thread but if someone runs into this problem this is how I solved it:

app.directive('emailInput', function(){
  return {
      restrict: 'E',
      templateUrl: 'template.html',
      controller:function($scope){
         $scope.isInvalid = function(){
            return $scope.myelem.data().$ngModelController.$invalid;
         }
      },
      link: function(scope, elem, attrs){
          $scope.myelem = $(elem).find("input");
      }
  }
}
Viktor Eriksson
  • 5,677
  • 3
  • 20
  • 24
0

Here is a directive that will set dirty to true even if nothing has been typed in.

By default $dirty is set if something is typed in and wouldn't show a required error message until the user submits. With this

function() {
    return function (scope, element, attrs) {
        $(element).blur(function () {
            scope.$apply(function() {
                var field = scope.$eval(attrs.makeDirty);
                field.$dirty = true;
            });
        });
    };

HTML:

<label class="fieldLabel" for="confirmEmail">Confirm Email*</label>
<input type="text" id="confirmEmail" name="confirmEmail" ng-model="confirmEmail" ng-pattern="{{Ctrl.Model.Email.EmailAddress}}" required make-dirty="form.confirmEmail">
<span class="error" ng-show="form.confirmEmail.$error.pattern && form.confirmEmail.$dirty">Emails must match</span>
<span class="error" ng-show="form.confirmEmail.$error.required && (form.$submitted || form.confirmEmail.$dirty)">Confirm your email</span>

That allows me to give feedback as the user is filling out or tabbing on the form.

Base33
  • 3,167
  • 2
  • 27
  • 31
0

Let me give you another way to do it, it can be useful in some cases

link: function (scope, element, attrs, formCtrl) {
    scope.fileSizeError=false;
    scope.$watch(function () {
               return formCtrl.fileP.$error.maxSize;
             },function(newValue) {
                scope.fileSizeError=newValue;
             });          
}

In my case I was inside a directive that is used to upload a file so I needed to know the state of the var $error.maxSize in the template so I did in that way.

Felipez
  • 468
  • 5
  • 6