1

I have some doubts about AngularJS + Custom Validations, from what I have read and checked by myself:

  • AngularJS provides great helpers for simple field validation (ng-require, ...).
  • The best way to implement a single field custom validation is via directive (not polluting the controller).

My doubts come when we have custom business validations that impact on more than one filed. Let's check the following simple scenario:

simple UI flight status

  • We are editing a flight arrival status, fields: Status (landed / scheduled / delayed), comments (additional info abot the flight status).
  • The business validations that I want to apply is: comments fields is required only if status fields value is "Delayed".

    The way I have implemented it:

Directive + Service

  • Define a directive to take care of Status + Comments field changes (status via $watch).
  • This directive delegates the business validation into a service

    The benefits I think this approach is giving to me are:

  • My business service validation gets isolated.

  • I could easily add unit testing to that business validation.

  • I could reuse it and it doesn't depend on UI elements.

    I have compiled this sample in a JSFiddle (JSFiddle validation sample).

JS:


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


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 to arrival.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"
myApp.factory('formArrivalValidation',

   function () {
       return {
           validateInfoField: function (status, infoField) {
               var isOk = true;

               if (status == 3) {
                   if (infoField == undefined) {
                       isOk = false;
                   } else {
                       if (!infoField.length)
                           isOk = false;
                   }
               }

               return isOk;
           },

       };
   });

Is this a good approach to follow? Is there better and simpler way to achieve this?

1

2 Answers 2

2

Regarding this part - "The business validations that I want to apply is: comments fields is required only if status fields value is "Delayed". for comment field set ng-required="flight.status == 'DELAYED'"

Sign up to request clarification or add additional context in comments.

2 Comments

Hey! pretty smart solution. My concern here is that it's something that falls in the UI layer, we cannot easily add automated unit testing (e.g. some code changes and our unit test battery doesn't detect the change on the UI) on the other hand you get the solution in less than a line of code, quite smart.
To add onto this. You can put the validation in a method on the controller and then unit test the controller.... This should be the accepted answer.
0

Coming back to this question... one valid approach could be to write a directive that accepts as parameter a second value (e.g. comments is empty)

Comments

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.