bitovi / canui

CanJS and jQuerypp
http://bitovi.github.com/canui
MIT License
28 stars 23 forks source link

can.ui.Validate API #13

Open justinbmeyer opened 12 years ago

justinbmeyer commented 12 years ago

Purpose

https://github.com/jzaefferer/jquery-validation http://activeform.rubyforge.org/ https://docs.djangoproject.com/en/dev/ref/forms/validation/

Markup

Options

preventIncorrect: boolean // if true, doesn't let user continue with form until validation is fixed validateOnlyOnSubmit: boolean // if true, only perform validations when the user clicks submit, else while using the form

Events

validSubmit: We can't know for sure we're preventing the submit event before another event handler gets to it. We trigger "validSubmit" event, which users bind to instead of submit. Perform AJAX save after this event.

Methods

Theming

Examples

simple form:

<input name='firstname' type='text' />
<input name='email' type='text' />

// person is a can.Observe
var formValidator = new can.ui.FormValidator($('form'), {
  mapTo: person
});

form where name to observe attribute isn't one to one mapping:

var formValidator = new can.ui.FormValidator($('form'), {
  mapTo:{
    // can.Observe attribute sets initial state, saved to after change, validation if exists
    ".player.email": {observe: person, attr: "email"},
    // can.compute used for setting after change, getting for initial state, validate
    ".player.pct": percentDone,
    ".sex":  {observe: person, attr: "sex"},
    // doesn't correspond to any can.Observe, but still needs validation
    ".level": function(levelVal, formData){
       // if level doesn't pass, return some validation failed message
    }
  }
});

The way I see it now, there are 4 different cases to cover:

  1. A form element maps to a can.Observe attribute (which already has validations)
  2. A form element maps indirectly to a combination of can.Observe attributes through a can.compute (which needs to support validations)
  3. A form element maps to no can.Observe attributes, but still needs validation...in this case through a function. Its arguments include this el's value and the form's data as a whole.
  4. Every element in a form directly maps to a can.Observe (through the name attribute). In this case, just pass the observe in mapTo.

Need API support for all 4 cases.

Notes

Solution: Should the options map each input to a can.Observe object and attribute name?

Con: This would make the API verbose. This makes the simple case (a form that directly corresponds with can.Observe) harder than it needs to be. Pro: This would make usage flexible enough to cover complex cases.

3 options:

1) APi maps form to a single can.Observe (automatically each form element -> the can.Observe of the same name) 2) API maps to a single can.Observe, but maps each form element -> an attribute name on that can.Observe 3) API maps each form element -> a can.Observe instance and an attribute name on that instance

  1. Forms might have inputs that don't correspond to any can.Observe, but still need validation. Should we support this? In this case, the API would accept a validation function.

Yes, we probably should let users map a form element -> a function in the options. It'll work like can.Observe.static.validate.

  1. Should we provide lots of options for customizing the error display and placement? Or keep it simple?

By default just position the error to the right of the form element. If it finds something with class="error" put it there. Or let users provide that className.

  1. Users can bind to form submit. We can't know for sure we're preventing the submit event before another event handler gets to it. Therefore we might have to trigger "validSubmit" event or something similar, which users bind to and submit on.
  2. Should we support async validations (for making AJAX validation checks)? We'd need to provide callback funcs for validate methods. Probably don't support this at least for now.
retro commented 12 years ago

How about API that looks similar to this:

var formValidator = new can.ui.FormValidator($('form'), {
    validations: {
        "player.name"  : ["presence", function(){
            //function should return errors or nothing if valid
        }],
        "player.email" : ["presence", "email"],
        "team.phoneNumbers.*" : ["presence", function(){ // validate arrays easy.

        }]
    }
})

Pros:

  1. It's completely decoupled from the model layer
  2. It can validate any data structure
  3. It can validate data that doesn't belong to any observe / model

Cons:

  1. It's completely decoupled from the model layer. What if user uses setters on the model? Should we set the data on the model and validate it there?
amcdnl commented 12 years ago

API Proposal

Selector based matching.

var formValidator = new can.ui.FormValidator($('form'), {

    // selector based matching
    "#input-email": {
        // type for built-in validators
        type: "Email",

        // option attribute name matching
        attrName: "emailAddress",

        // optional custom validation
        validation:function(attr){
            // calls validation for type by super
            var validation = this._super(attr);

            // do your own validation
            if(attr != "austin@canjs.us") { 
                validation = false 
            }

            return validation;
    },

    // wildcard matching
    "*-email": {
        type: "Email"
    }

});
justinbmeyer commented 12 years ago

Validations should be on the model.

Sent from my iPhone

On Jun 29, 2012, at 10:05 AM, Austinreply@reply.github.com wrote:

API Proposal

Selector based matching.

var formValidator = new can.ui.FormValidator($('form'), {

   // selector based matching
   "#input-email": {
       // type for built-in validators
       type: "Email",

       // option attribute name matching
       attrName: "emailAddress",

       // optional custom validation
       validation:function(attr){
           // calls validation for type by super
           var validation = this._super(attr);

           // do your own validation
           if(attr != "austin@canjs.us") { 
               validation = false 
           }

           return validation;
   },

   // wildcard matching
   "*-email": {
       type: "Email"
   }

});


Reply to this email directly or view it on GitHub: https://github.com/jupiterjs/canui/issues/13#issuecomment-6662963

amcdnl commented 12 years ago

I would +1 for having them on observables too.

retro commented 12 years ago

This is something Brian and me came up with:

    validations: {
        "player.*": modelInstance.validations(),
        "team.phoneNumbers.*" : ["presence", function(){ // validate arrays easy.

        }],
            "comment" : "presence"
    }
})

In this case everything under "player" namespace will be validated with the model's validations, while it allows other stuff to be validated directly on the validator

justinbmeyer commented 12 years ago

I think can.compute needs to work here. Also, there is no instance validations method.

Sent from my iPhone

On Jun 29, 2012, at 10:14 AM, Mihael Konjevićreply@reply.github.com wrote:

This is something Brian and me came up with:

   validations: {
       "player.*": modelInstance.validations(),
       "team.phoneNumbers.*" : ["presence", function(){ // validate arrays easy.

       }],
           "comment" : "presence"
   }

})

In this case everything under "player" namespace will be validated with the model's validations, while it allows other stuff to be validated directly on the validator


Reply to this email directly or view it on GitHub: https://github.com/jupiterjs/canui/issues/13#issuecomment-6663925

justinbmeyer commented 12 years ago

Aren't validations a proposal for HTML5? Should we place nice with these if validations are given from the outside?

Also, a form is really about making changes. How does this work non input widgets like my slider?

How would translations work?

How does this work with live-binding?

And there's 3 cases of validations in some ways:

  1. preventing incorrect input from being entered
  2. allowing incorrect input but giving a warning while it's being entered
  3. allowing incorrect input but not giving a warning until the "submit" is fired

Also, how would conditional validations work? For example, I only care about "brogrammer" level if "male" is selected.

I think we need to decide if validations should really even be a model thing.

retro commented 12 years ago

For the slider you could use hidden field and update it's value.

But, I think that more important question where are we going to define the validations. If they are on the model layer, then we should update validations plugin so it works better with nested data. If they are on form / validator object level, then that object should have ability to automatically update model(s) that are related to that form.

amcdnl commented 12 years ago

@Justin - Ya, there is already support in modern browsers for some of it.

Found this: http://www.the-art-of-web.com/html/html5-form-validation/