Sunday, October 14, 2012

Validating A Custom Control in Angular JS with Angular's Built-In Validations

So I recently ran into an issue where I wanted to create a custom control that was basically two inputs that output to one piece of data. The issue was that sometimes it was required, and other times it wasn't, but I didn't want to require just one field or the other. I wanted the whole control to register as required. The control in question was a month/year date control, which is was a little complicated, logic-wise. So to illustrate the basic idea, I put together a quick plunker of the idea with a custom addition control.

Here are the basic steps to follow:
  1. Create a new directive.
  2. The directive must require: 'ngModel', this does most of the angular wiring for you.
  3. The link function of the model must do two things:
    1. Set a $watch on the item in the directive's scope you want to use as the control's value and use $setViewValue() to set the value of the view for ngModel.
    2. Set the $name for ngModel. This is used for validation purposes like myForm.fieldName.$error.required

Here is the directive for the custom addition control.

app.directive('blCustomSum', function(){
  return {
    restrict: 'E',
    require: 'ngModel', //important!
      '<div>' + 
        'A <input ng-model="a" ng-change="add()"/> +  ' + 
        'B <input ng-model="b" ng-change="add()"/> = ' +
        '{{c}}' +
    controller: function($scope) {
      // just a controller method to add the numbers
      // and set the value of c.
      $scope.add = function(){
        if(!isNaN($scope.a) && !isNaN($scope.b)) {
          $scope.c = parseFloat($scope.a) + parseFloat($scope.b);
        } else{
          $scope.c = '';
    link: function(scope, elem, attr, ctrl) {
      // ctrl is what has been set up by the ngModel directive.
      // wire up c to be the value of ngModel for this control
      scope.$watch('c', function(value) {
      // set the name for ngModel and validation.
      ctrl.$name =;

And here is an example of how to use it in template markup:

<body ng-controller="MainCtrl">
<form name="myForm">  
  <bl-custom-sum ng-model="sum" name="sum" required></bl-custom-sum><br/>
  <span ng-show="myForm.sum.$error.required">required</span><br/>
  form valid: {{myForm.$valid}}


  1. Uncaught ReferenceError: app is not defined loginscript.js:3

    Error: Argument 'MainCtrl' is not a function, got undefined

    1. Do you have a plunker or fiddle? Chances are you never defined a controller for MainCtrl. the bit above that says ng-controller="MainCtrl" defines a controller for that area. That means in your code you'll need to have this at the very minimum: app.controller('MainCtrl', function($scope) {});

  2. Hi blesh,

    I was having a look into the plunker.I think {{c}} in the template should be {{sum}}, as sum is the model of the custom control. And c is just a computed scope value based on the two input field. Updating the viewValue of the custom control using the varible c will update the model of the custom control. So it is suitable to print {{sum}} while the variable c is updated.

    1. While that would work in this case, the problem is that if you do that and you're passing a different expression to the custom directive, it will break. It's best to use variables isolated to your directive.

      In other words, imagine that in the html using the directive it was as such:

      <bl-custom-sum ng-model="foo" >

      Now your directive is broken because it's expecting "sum" to be passed in via ng-model.


This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)