Angular Object Validator: Enforcing object types in an Angular.js application

As anyone with JavaScript experience will tell you, occasionally the Wild West of coding languages falls a little short in critical areas by not enforcing object types. On a small Angular application, where all the developers have intimate knowledge of the code base, this may not be an issue. However, as the project scales, and new people begin working in the solution, you may find yourself wishing you had a way to enforce object types within your more critical functions. At this point, you may be asking yourself “What about TypeScript?”. If you have the option to start fresh, and know that your application will require type enforcement, TypeScript is definitely the logical answer. However, there are times in which TypeScript might not be an option. (legacy code, etc.)

To help with this issue, here at Sigao we have created a solution specifically for Angular.js applications called Angular-Object-Validator. This simple service will provide a single entry point for loading and validating pre-defined objects against potentially dynamic objects within your application.

Installing Angular Object Validator

Via Bower:

bower install angular-object-validator --save

 

Once the package and its dependencies have been added to your project, your angular application should have access to a module called “ObjectValidator” which you will need to inject into your application.  Each controller that will use the validator needs to be injected with a variable called “objectValidator

 

Usage

Setting up the validator

Angular services behave as singletons, meaning once data has been changed inside the service, that data is accessible everywhere in the application that the service is used. Because of this, it is recommended to load all of your object models in one location via the command objectvalidator.addClass(“Class Name”, _yourModelObject).

 

Testing

Once the validator has ingested all of the classes, you can now validate objects at any point that you have injected the validator. To test a specific class, all you need to do is use objectValidator.test(“Class Name”, yourObjectVariable). This will return true for valid, and false for invalid

 

Configuration

Optionally, you may change the behavior of the validator by sending a configuration object to  objectValidator.config({}).  Object can contain a property called checkProto and matchPropertyCount, both of which are boolean values

 

Examples

1. Configuration property: checkProto

Default value: true. This configuration determines if prototyped properties are validated against the model.  Below is an example of 2 objects and how they compare with checkProto:

//Object 1
objectValidator.addclass("CheckProtoExample", {
     something:"",
     somethingElse:[]
});

//Object 2
function example() {
     this.something = "";
}
example.prototype = {somethingElse: []};
var testObject = new example();

objectvalidator.config({checkProto:true});
objectvalidator.test("CheckProtoExample", testObject); //Returns: true
objectvalidator.config({checkProto:false});
objectvalidator.test("CheckProtoExample", testObject); //Returns: false

2. Configuration property: matchPropertyCount

Default value: true.  This configuration decides if the validator matches the exact properties in the model, or if the tested object can have additional properties beyond those in the model.  Note that the tested object is always required to have at least all of the model objects properties.

objectValidator.addClass("matchPropertyCountExample", {
            test1: "",
            test2:[],
            test3:true,
            test4:{}
});

var testObject = {
            test1: "",
            test2:[],
            test3:true,
            test4:{},
            test5:"something",
            test6:{}
        }

objectValidator.config({matchPropertyCount: true});
objectValidator.test("matchPropertyCountExample", testObject);//Result: false
objectValidator.config({matchPropertyCount: false});
objectValidator.test("matchPropertyCountExample", testObject);//Result: true

 

3. Generic controller mock up

The following example is a mock up of a generic controller for interacting with a database. Keep in mind, because this is an example, it has not be tested and will likely need some extra work to be used. However, it illustrates how to effectively use the object validator in your application.

Module setup

(function () {
    "use strict";

    angular.module('YourModule', []).directive('YourDirective', yourDirective);
    yourDirective.$inject = ['$http', 'objectValidator'];

    function yourDirective($http, objectValidator) {
    
        //Your code goes here

    };
})();

Here we have the beginnings of our module, highlighting how to inject the object validator into the controller. All of the following code examples are placed inside the main controller function of this module, in the order that they are discussed.

 

Setting up the validator

//setting validator to match exact number of properties on objects as well as prototyped values
objectValidator.config({checkProto: true, matchPropertyCount: true});

//Load models into validator.  Because services are singletons, this can be done
//in one main controller for all of the critical objects in your site.  For the sake
//of time, we've defined them here
//Validator will use the type of the model property value for the validation
objectValidator.addClass("getDto",{
    status: 0,
    result: {
        property1: 0,
        property2: [],
        property3: ""
    }
});
objectValidator.addClass("postDto",{
    data: []
});

Now we are defining the models and configuring the validator. In larger applications it is recommended that loading the object models happens in one location for the whole application, rather than inside the controller. However, for this example we decided to simplify the process.  Keep in mind that the models must be defined using the type of variable you require.

Defining the functions

//Service model
var service = {
 getData: getData,
 setData: setDat
};
return service;

function getData() {
            return $http.get("http://yourURL.com/api/getData")
                .then(handleReturn)
                .catch(function (message) {
                    console.error(message);
                });
            function handleReturn(data, status, headers, config) {
                //make sure that inbound data matches structure of _dtoModel
                if (objectValidator.validateObject("getDto", data)) {
                    return data;
                } else {
                    console.error("YourDirective.getData received an invalid object format");
                    return null;
                }
            }
}
function setData(data) {
            if (objectValidator.validateObject("postDto",data)) {
                return $http.get("http://yourURL.com/api/setData")
                    .then(handleReturn)
                    .catch(function (message) {
                        console.error(message);
                    });
            } else {
                console.error("YourDirective.setData received an invalid object format");
                return null;
            
            function handleReturn(data, status, headers, config) {
                //optionally, you can validate the post return if you'd like
                return data;
            }
        }
}

Lastly, we define the functions for getting and setting the hypothetical data. We use the validator to compare the objects being sent and received against the models that we expect to send and receive. If they do no match, we handle the exception and alert the user.

 

Documentation

Name Description Argument Type(s) Returns
test() Tests an object against a pre loaded class String: Class Name,
Object: model
Bool: validation result
type() Returns the type of variable sent to it Var: input variable String: type of variable
addClass() Adds a class model into the validator to test data against String: Class Name,
Object: model
getClass() Gets a class that has been loaded into the validator String: model name Object: model object
removeClass()Gets a class that has been loaded into the validator Removes a class that has been loaded into the validator String: model name
getAllClasses() Gets all of the classes that have been loaded into the validator Object: object that contains all classes
removeAllClasses() Removes all classes in the validator
config() Sets configuration settings within the validator Object: Configuration object that contains 1-2 properties.  checkProto, and/or matchPropertyCount