0

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 :)

ramblinjan
  • 6,578
  • 3
  • 30
  • 38
Ankur Aggarwal
  • 2,993
  • 5
  • 30
  • 56
  • Can you give us an example of the formObject? I'm having trouble understanding how the form can put data in your model instance and have it be understood by the rest of your application. – air_hadoken Mar 31 '14 at 22:44
  • @air_hadoken I am using `can.deparam(form.serailize())` which will return me a form object. Now I want to validate the emptiness of the form fields which are stored as property value pair in the object. – Ankur Aggarwal Apr 01 '14 at 05:55
  • @air_hadoken updated the question with the code – Ankur Aggarwal Apr 01 '14 at 18:26

2 Answers2

2

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');
Ankur Aggarwal
  • 2,993
  • 5
  • 30
  • 56
Cherif BOUCHELAGHEM
  • 1,126
  • 2
  • 14
  • 21
  • I cannot do `this.validatePresenceOf(['fname','lname']);` as formObject is getting generated dynamically because there are multiple forms. So i don't knwo what will be my object's attribute. Can I have access to my object in init method itself? – Ankur Aggarwal Mar 30 '14 at 21:56
  • You can access attributes by using attr() function myMap.attr(), loop and apply validate function http://canjs.com/docs/can.Map.validations.static.validate.html – Cherif BOUCHELAGHEM Mar 30 '14 at 23:15
  • init is the static property of can.Map and attr is the instance property. How can I access attr inside the static constructor itself. Correct me if I am wrong!!! – Ankur Aggarwal Mar 30 '14 at 23:33
  • 1
    You can't access the instance in the constructor init method. I'm afraid that you need some other workaround. You can however add an instance init method, but if you add the validation there you will end up adding multiple validations for the same thing (one for each object you instantiate). – Sebastian Mar 31 '14 at 03:30
  • @CherifBOUCHELAGHEM updated the question with the code – Ankur Aggarwal Apr 01 '14 at 18:27
2

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/

air_hadoken
  • 611
  • 4
  • 5