Angularjs validation behaves differently for 1.3.0 and 1.2.6

Issue

I wish to create a custom directive, which renders as an input type element. The directive should reuse the angularjs validation framework. Following is the custom-input directive in action which I have created:

<!doctype html>
<html ng-app="validationApp">
<body>
  <div class="container" ng-controller="ValidationController as validationController">
    <form name="myForm">
        {{employee | json}}
    <custom-input ng-required="true" ng-model="employee.name" name="employee.name" id="employeeName" ng-pattern="/^[0-9]{1,7}$/"/></custom-input>
    <span ng-show="myForm['employee.name'].$error.required">This is a required field</span>
    <span ng-show="myForm['employee.name'].$error.pattern">This is a invalid field</span>
    </form>
  </div>
  <script type="text/ng-template" id="/templates/customInput.html">
    <div>
        <input type="text" name="{{name}}" ng-model="newInput" id="{{id}}">
    </div>
  </script>
  <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.js"></script>
</body>

</html>

The javascript corresponding to this is:

angular.module('validationApp', [])
.controller("ValidationController", function(){

})
.directive("customInput", function(){
    return {
        restrict: "E",
        require : "ngModel",
        replace: "true",
        templateUrl : "/templates/customInput.html",
        scope : {
            id : "@", //bind id to scope
            name : "@" //bind name to scope
        },
        link: function(scope, element, attrs, ngModelCtrl){
            //When newInput is updated, update the model of original input
             scope.$watch('newInput', function(newValue){
                ngModelCtrl.$setViewValue(newValue);
            });

            //On first load, get the initial value of original input's model and assign it to new input's model
            ngModelCtrl.$render = function(){
                var viewValue = ngModelCtrl.$viewValue;
                if(viewValue){
                    scope.newInput = viewValue;
                }
            }
        }
    }
});

I am trying to apply ng-required and ng-pattern validation on this custom-input. I am running into two problems:

  1. In angularjs 1.2.6, I am able to fire ng-required validation in the custom-input but in 1.3.0, the validation is not getting fired.
  2. I am unable to fire ng-pattern validation in both versions.

My understanding is that $setViewValue of ngModelController will fire all the validations. The above is a contrived example, my actual use case is to create a custom directive which renders three input boxes for SSN.

Following is the plunker link for 1.2.6 and 1.3.0 respectively:

Angularjs 1.2.6
Angularjs 1.3.0

Solution

  1. To get the ng-required to work, don’t have the same name on the input of the inner input form. I appears that in Angular 1.3, this overrides the registered ngModelController attached to your custom-input.

    So the template for the directive can be

    <div>
      <input type="text" ng-model="newInput" id="{{id}}">
    </div>
    

    This can be seen working at http://plnkr.co/edit/TqMkxV?p=preview.

    (Also, not sure why you need id here…)

  2. I think (but I’m not sure) in 1.2.6 ngPattern only works on input elements, while in 1.3 it’s a separate directive that integrates with ngModel. Also, to get it to work in 1.3

    • Use pattern instead of ng-pattern, even though the docs suggest ng-pattern should work.
    • If you’re writing the pattern directly in the template, don’t wrap it in //. So a correct use of it looking for exactly 5 digits would be pattern="^\d{5}$". The docs are misleading in this.

      Your modified 1.3 example, with a working example of pattern can be seen at http://plnkr.co/edit/1zSiJI?p=preview

Edit after comments:

  1. If you do want the name to appear on the inner directive, and be the same as the parent elements name (e.g. for submitting using standard full page POST to server), you can wrap it in a named ngForm. This will ensure that its controller won’t override the parent one in the scope.

    <div>
      <div ng-form name="customInput">
        <input type="text" ng-model="newInput" id="{{id}}" name="{{name}}">
      </div>
    </div>
    

    This can be seen at http://plnkr.co/edit/Jrk63A?p=preview

Answered By – Michal Charemza

Answer Checked By – Terry (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.