forms-js / discussions

6 stars 0 forks source link

Validators API #5

Open mohsen1 opened 9 years ago

mohsen1 commented 9 years ago

My proposal:

each schema can have a validators property which is an array of validator objects. A validator object have the following properties:

The validation functions will get executed with the array order and if all validations passed the entry is valid. The validator starts with leafs of the Schema tree and crawls up to the root of the schema.

Example

{
  type: "object",

  "properties": {
    "email": {
      "type": "email",
      "validators": [
        {
          message: 'You were unlucky!',
          fn: function(){
            return Math.random() > 0.4; // :D
          }
        }
      ]
    }, 
    "password": {
      "type": "password",
      "validators": [
        {
          message: 'Password can not have letter M in it!',
          fn: function(password) {
            return password.indexOf('M') === -1; // ?!?
          }
      ]
    }
  }, 

  "validators": [{
    message: 'email and password should not be equal',
    fn: function(obj) {
      return obj.password != obj.email;
    }
  }]
}
bvaughn commented 9 years ago

Might be nice for custom validation functions to be allowed to reject with an error message that overrides the one specified in message.

bvaughn commented 9 years ago

In general, I also prefer more descriptive attribute names (ex "function" vs "fn"). I realize that function is one of those special words, but maybe something like validator? or method?

mohsen1 commented 9 years ago

How about this: You can return (or resolve to) boolean or string(custom error)

I'm open for a replacing fn with something more meaningful

kentcdodds commented 9 years ago

I must say that I am against the JSON schema-like format of having field configuration separate from the validation config. I think that field config should be able to stand on its own. It is much easier to build abstractions for different field types this way.

bvaughn commented 9 years ago

Could you clarify a little Kent?

Are you proposing that the full form markup should be generated from a single configuration object? Or something else...

mohsen1 commented 9 years ago

@kentcdodds I'm not sure if I understand your concern correctly. Each filed can have it's own list of validators and is completely independent from it's parent and siblings. For example email itself in my example is a complete schema. Being recursive is a key feature of JSON Schema

{
      "type": "email",
      "validators": [
        {
          message: 'You were unlucky!',
          fn: function(){
            return Math.random() > 0.4; // :D
          }
        }
      ]
    }
kentcdodds commented 9 years ago

Ah, I think I misread that. Sounds good to me. I don't mind the fn abbreviation. In angular-formly, I use expression because it can either be a string expression (as you'd see in an angular template) or a function. But fn I think is pretty well accepted by people. Also, would probably be a good idea to make it be an object instead of an array. That way you can override validators. This follows the $validators api of angular which I think was a good decision.

mohsen1 commented 9 years ago

@kentcdodds overriding validators sounds interesting! Can you write an example of that? I'm not sure I did understand it right.

kentcdodds commented 9 years ago

Type definition:

{
  name: 'foo',
  defaultOptions: {
    validators: {
      isFoo: function(val) {
        return val === 'foo';
      }
    }
  }
}

then later, in a field definition:

{
  type: 'foo',
  validators: {
    isFoo: {
      message: 'That is not foo!',
      pattern: /^foo$/ // <-- just kinda threw that in there, I think we should allow them to specify a fn, a pattern and anything else that makes sense :-)
    }
  }
}
kentcdodds commented 9 years ago

Oh, also notice that in the type definition, the validator isFoo is a function. In this scenario, it would be the same as:

{
  isFoo: {
    message: null,
    fn: function(val) {
      return val === 'foo';
    }
  }
}
bvaughn commented 9 years ago

+1 for using named validations. I like the flexibility of being able to override requirements for a specific context.

Although this kind of raises another question- if our validations are spread across multiple schema files- how do we pass them to form-js?

mohsen1 commented 9 years ago

forms-js should not solve that problem. User have to pass everything in one object or write custom validators that call third party validators.

bvaughn commented 9 years ago

Sorry, let me rephrase. My question is higher-level than that: what interface does forms-js expose for validations? (How do you pass any validation to forms-js.)

mohsen1 commented 9 years ago

oh you mean global static validators? I think we should avoid that. It's going to lead to a lot of surprises. The most global validators list is the one in the schema root

bvaughn commented 9 years ago

No, sorry. We're still misunderstanding each other.

Assertion: form-js will help with the generation and validation of forms. To instrument validation, it will need to be provided with some sort of validation configuration (POJO or JSON schema).

Question: If we go with a JSON schema, how do we pass that schema to forms-js?

mchapman commented 9 years ago

@bvaughn I think we are no heading back into the area of https://github.com/forms-js/discussions/issues/2 (although I just put a curve ball in there).

I would like to see support (as in forms-angular) for schemas hard coded into a controller (in angular speak) and for schemas requested from the server on the fly. How they are built up on the server is outside the scope of this project (I will predictably be building mine from Mongoose schemas).

mohsen1 commented 9 years ago

We are extending the JSON Schema. The extension contains functions so it's not JSON anymore and it's POJO. All validation logic should be included in the Schema itself.

Here is the example of what I mean:

<forms-js
  action="/"
  method="post"
  schema="{
    type: 'string',
    title: 'Name'
    validators: {
      noScript: {fn: function(name) {
        return name.indexOf('<script>') === -1;
      },
      message: 'No scripts'
      }
    }
  }">
    <button type="submit">Submit</button>
</forms-js>

That should make a form like this:

<forms-js action="/" method="post">
  Name: <input type="string" title="Name"> <span style="display:none">No Scripts!</span> 
  <button type="submit">Submit</button>
</forms-js>

There will be event listeners for input change event and so on...

mohsen1 commented 9 years ago

Note that in my example I'm using forms-js like <form>. It's because we will make forms-js web component extend form element. 😄

bvaughn commented 9 years ago

I strongly disagree with auto-generating the forms from the validation rules. This limits our users too much and only allows for very simple/plain forms to be generated.

We are extending the JSON Schema. The extension contains functions so it's not JSON anymore and it's POJO. All validation logic should be included in the Schema itself.

I think we're also still not saying the same thing.

Yes validation rules will be in the schema. Validation logic might also be there (if people are using our subset of the schema). But something should be instrumenting when the validation rules are applied and governing higher-level things like "is the form valid?" "can the form be submitted?"