2
\$\begingroup\$

In order to perform form validation, I have created a custom object:

define(["jquery", "validate", "activities/new/common/components/BaseFormdata"], 
    function($, Validate, BaseFormData){ 

    var FormData = function(options){ 

        this.defaults = { 
            name: "", 
            privacy: "", 
            price: "", 
            short_description: "", 
            long_description: "" 
        }; 

        //Constraints to be used by the validation library 
        this.constraints = {
            name: { 
                presence: true, 
                length: { maximum: 100, minimum: 1 }
            }, 
            privacy: {
                presence: true
            }, 
            short_description: { 
                short_description: true, 
                length: { maximum: 20 }
            }, 
            max_description: { 
                length: { maximum: 1000 } 
            } 
        }; 

        this.formData = $.extend({}, this.defaults, options); 

    }; 

    //Inherit from the BaseFormData component 
    FormData.prototype = new BaseFormData(); 

    return FormData; 
});  

The base FormData object:

define(["underscore", "validate"], function(_, Validate){

    //Defines a Base FormData object that modules can use to inherit from 
    function BaseFormData(){}; 

    /*@param: 
     * object containing to be validated properties 
     * @return: 
     * Success returns false (no error), Error returns an error object 
     */
    BaseFormData.prototype.validateProperties = function(objToValidate){ 

        var constraints = this.getConstraints(objToValidate); 

        var status = validate(objToValidate, constraints, {format: "detailed"}); 
        if(status === undefined){ 
            //There is no error 
            for(var key in objToValidate){ 
                if (this.formData.hasOwnProperty(key)) { 
                    //Set the value on the this.formData object 
                    this.setProperty(key, objToValidate[key]); 
                }; 
            }; 
            //Return no error 
            return false; 

        } else { 
            //An error object is returned 
            return status; 
        } 
    }; 

    //Set property on object 
    BaseFormData.prototype.setProperty = function(key, value){ 
        this.formData[key] = value; 
    }; 

    //Get relevant constraints from constraints object 
    BaseFormData.prototype.getConstraints = function(objToValidate){ 
        //Get keys in array 
        var arrKeys = _.allKeys(this.constraints); 
        var constraints = {}; 
        _.each(arrKeys, _.bind(function(key){ 
            if(objToValidate.hasOwnProperty(key)){ 
                constraints[key] = this.constraints[key]; 
            }; 
        }, this)); 

        return constraints; 
    }; 

    return BaseFormData; 
});

Is it bad practice to set the form properties immediately on the FormData object? Currently, I have per form one custom FormData object that inherits from the Base FormData object, for which I have set custom properties depending on the form components.

I wonder if it would be better to inject the form fields as a collection of objects in a single FormData object (instead of creating one for each form); this would allow me to define only the base FormData object, but it would require the calling function to also send the constraints along (which is in my case a presenter object that coordinates between the different views that manage the form).

Which is the best approach architectural-wise?

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

First of all, I think creating a constructor for each form type is overkill. All you need from that subclass are defaults and constraints. Why not remove all that wrapper code and just have the module return defaults and constraints?

// FormDataRules.js
define(function(require, exports, module){
  exports.defaults = { ... };
  exports.constraints = { ... };
});

Then create a validation module that contains all your validation APIs. Doing the above, setProperty falls off since you'll be operating on plain objects.

getConstraints also falls off leaving you with validateProperties which we can just rename to validate - which is a huge operation which just delegates checking to subfunctions inside the module.

// Validator.js
define(function(require, exports, module){

  exports._validateNumbers = function(data, constraint){ ... }
  exports._validatePresence = function(data, constraints){ ... }

  exports.validate = function(data, constraints){
    /* Huge operation to validate each item with each constraint it comes */
  };
});

Here's sample code using above.

define(function(require){
  var Validator = require('Validator');
  var FormDataRules = require('FormDataRules');
  var _ = require('lodash');

  var dataWithDefaults = _.defaults({
    // instance stuff
  }, FormDataRules.defaults);

  var isDataPassed = Validator.validate(dataWithDefaults, FormDataRules.constraints);

});

This makes everything easy to test. FormDataRules are just objects you can simply check values. Validator is a bunch of functions you can simply test by throwing values at it. All the data that mutates lives in a plain object that we have hold to (dataWithDefaults), and not something internal to the validator nor the rules.

\$\endgroup\$
0
0
\$\begingroup\$

Is it bad practice to set the form properties immediately on the FormData object? Currently, I have per form one custom FormData object that inherits from the Base FormData object, for which I have set custom properties depending on the form components.

No, is not bad practice, It deepens of your design. If you have some other form with different data, what happens with the base FormData?

heritage es a powerful weapon, but is single shot.

I wonder if it would be better to inject the form fields as a collection of objects in a single FormData object (instead of creating one for each form); this would allow me to define only the base FormData object, but it would require the calling function to also send the constraints along (which is in my case a presenter object that coordinates between the different views that manage the form).

I solved a similar problem extracting with jquery the key/values of the fields, and transforming to a plain object/dictionary, and then mixed up with a rich model of data.

I do something (more or less) like:

var formData = {}    
$form.find('type[input]').each(function (index, element) {
   formData[element.name] = element.value
});
var mySuperModel = new MyModel(formData);

of course is an example, its not really working, is an approach of what i did.

Which is the best approach architectural-wise?

The most popular way to validate a form today, is thorough a jquery plugin like validation. or similar, I'm not sure if is the BEST option, because this resolve the binding with inputs and the validation, the messages and stuff but there need a lot of configuration.

The main problem, when you validate a form is the interaction with the user and flag the wrong fields to the user change it. In your approach, you split your data (model) from the form (view) and have the validations only in the model, thats is a good decision, and don't mix the validations. But need to be bind to the form fields and message errors, you should work in some way to automatize that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.