Angular: Custom Validations

by Braulio

HTML

<script src="http://code.angularjs.org/angular-1.0.1.js"></script>
<div ng-controller="MyCtrl">
    <p>Sample custom validation involving more than one field.</p>
    <br/>
    <p>Simple custom validation sample: Comments fields cannot be empty if flight Status selected value is delayed</p>
    <br/>
    <p>Challenges: Two fields involved, non empty comments validation only applies if flight status is delayed, we have to watch for flight status and comments, we want as well to isolate business validations in a easy to test and encapsulated class</p>
   
    
    <form class="form-horizontal" accept-charset="utf-8" name="editArrivalForm">
        <br/> <span>Flight Status</span>

        <select name="cbstatus" ng-model="arrival.status">
            <option value="1">Landed</option>
            <option value="2">Scheduled</option>
            <option value="3">Delayed</option>
        </select>
        <br/> <span>Comments: </span>

        <input type="text" name="txInfo" ng-model="arrival.info" validateinfofield="">
        <span ng-show='editArrivalForm.txInfo.$error.validInfo'>A Info cannot be null when delayed!</span> 
            
        <br/>
        <button type="submit" ng-disabled="editArrivalForm.$invalid">Update</button>                                            
    </form>                
</div>

JavaScript

var myApp = angular.module('myApp', []);


// =Controller
function MyCtrl($scope) {    
    $scope.arrival = {
        "id": 1,
        "originAirport": "Malaga (AGP)",
        "flightNumber": "Iberia 132",
        "dueAt": "2013-05-26T12:10:10",
        "status": 2,
        "info": "test"
    }
}
// #Controller

// Directive Comments fields validation
// Detect changes: Comments fields and Flight Status combo
// Delegate real validatin into a Service
myApp.directive('validateinfofield', ['formArrivalValidation', function (formArrivalValidation) {
    return {
        require: "ngModel",
        link: function(scope, elm, attrs, ctrl) {


            ctrl.$parsers.unshift(function(viewValue){
                // Empty? Let's check status
                //if (!viewValue.length && scope.arrival.status == 3) {
                if(formArrivalValidation.validateInfoField(scope.arrival.status, viewValue)) {
                    ctrl.$setValidity('validInfo', true);
                } else {
                    ctrl.$setValidity('validInfo', false);
                }
            });
            
            // Let's add a watch toi arrivel.status if the values change we need to 
            // reevaluate, if comments is empty and status is delayes display error
            scope.$watch('arrival.status', function (newValue, oldValue) {
                if (formArrivalValidation.validateInfoField(newValue, scope.editArrivalForm.txInfo.$viewValue)) {
                    ctrl.$setValidity('validInfo', true);
                } else {
                    ctrl.$setValidity('validInfo', false);
                }
            });

        }
    };
}]);

// Validation Service, limited to our "business language"
// it only takes care about our rules, doesn't know anything 
// about directives or any Angular Jargon
// The idea here is to have an easy to test service, isolated,
// and that even it could be reused in a nodejs app
myApp.factory('formArrivalValidation',

   function () {
  ...