Validate Dynamic Object using can.Map.Validate

1k views Asked by At

I was looking for form validation in CanJS and came to know about can.Map.Validate plugin http://canjs.com/docs/can.Map.validations.prototype.errors.html

In the below example taken from the documentation itself, we need to give the properties of the object like 'dueDate' before the creating of the object itself

Task = can.Map.extend({
  init : function(){
    this.validatePresenceOf("dueDate")
  }
},{});
var task = new Task(),
    errors = task.errors()

Now my requirement is I want to build a Map which checks whether my field attribute is empty and returns me a suitable error. But the problem is, there are multiple forms on my page and formObject gets generated dynamically. Now in the above scenario before the creation of object itself, you need to allot the property in init method on which validation needs to be performed. It is not good for my requirement as the object is getting build and properties are unknown before the creation of the object. I searched over the web a lot but unable to crack it.

Providing the code for better understanding EJS File: login.ejs

<form id="registrationForm" name="registrationForm" method="POST">

     <input type="text" name="userName" placeholder="UserName" class="inputFieldStyle"></input>
     <input type="password" name="password" placeholder="Password" class="inputFieldStyle"></input>
     <input type="password" name="confirmPassword" placeholder="Confirm Password" class="inputFieldStyle"></input>
     <input type="email" name="email" placeholder="Email" class="inputFieldStyle"></input>
     <input type="button" name="registrationSubmit" value="Register" class="registrationLoginButton"></input>
     <input type="hidden" name="formType" value="registration"></input>
</form>

    <form id="loginForm" name="loginForm" method="POST">
        <input type="userName" name="userName" placeholder="UserName or Email" class="inputFieldStyle"></input>
        <input type="password" name="password" placeholder="Password" class="inputFieldStyle"></input>
         <input type="button" name="loginSubmit" value="Login" class="registrationLoginButton"></input>
          <input type="hidden" name="formType" value="login"></input>
    </form> 

Controller:

var LoginController=can.Control({
    defaults: {view:'login.ejs'}
},
{
    init:function(element,options)
    {
        var fragment=can.view(this.options.view);
        this.element.html(fragment)
    },
    'input[name="registrationSubmit"],input[name="loginSubmit"] click':function(el,ev)
    {
        ev.preventDefault();
        var formDOMObject=el.parent('form');
        var formValueObject=can.deparam(formDOMObject.serialize());
               /*Need to validate formValueObject. Problem is either formDOMObject can be resgitrationForm or LoginForm. Now both have different properties, So i cannot provide predefined properties in init method */
    }
});

Is there any way of validating the dynamic object properties using ca.Map.Validate plugin? How can I access the instance object passed inside the init method?

Thanks in advance :)

2

There are 2 answers

4
air_hadoken On BEST ANSWER

Seems like what you want is to make temporary, throwaway classes with validations that are built from an existing object, which can then be observed for errors.

Here's an example showing that the validations are observable and change dynamically with the source can.Map

can.Map("DynamicFormValidator", {
    newInstance : function(data) {
        var cls = this.extend();
        can.each(data.serialize(), function(val, key) {
            cls.validatePresenceOf(key);
        });
        //cls.prototypes.data = data;
        return can.Map.newInstance.call(cls, data);
    }
}, {
    init : function(data) {
        var that = this;
        this.attr("data", data);
        this.bind("change", function(ev, attr, how, newVal) {
            if(attr.indexOf('data.') === 0) {
                that.attr(attr.substr(5), newVal);
            }
        })
    }
    , computed_errors : can.compute(function() { return this.errors(); })
});

See it in action at http://jsfiddle.net/air_hadoken/8rK2n/2/

5
Cherif BOUCHELAGHEM On

You need to make ,can.map (or compute) for the state and mustache helpers to reflect the state in the dom for this I use an approach from sebastion proto http://sporto.github.io/blog/2014/03/04/form-validations-with-canjs/

and here is the jsfiddle

   can.fixture({
    "GET contacts":function(){
        return [{'fname':'Cherif','lname':'BOUCHELAGHEM'}]
    }
})

can.Model.extend('Contact',{
    init:function(){
          this.validatePresenceOf(['fname','lname']);
    },
    findAll: 'GET contacts'
},{});

can.Control.extend('ContactForm',{
    init:function(el,options){
        this.contact=new Contact();
        this.errors=new can.Map({});
        this.element.html(can.view('validation',contact:this.contact,                                              errors:this.errors}));
    },

    'form submit': function () {
        var errors = this.contact.errors();
        this.errors.attr(errors, true);  
        return false;
    }
});
Mustache.registerHelper('showErrors', function (errors, prop) {
  var attr = errors.attr(prop);
  if (attr) {
    return prop + ' ' + attr[0];
  } else {
    return '';  
  }
});
new ContactForm('#val');