开发者

Javascript form-validation framework: Request for Review

I wasn't sure if I could ask this kind of question, but after seeing this on Meta Stackoverflow, it looks like this kind of question is ok. Well, onto my question:

A few months ago I wrote a validation framework in Javascript. I know that there already exist validation frameworks like jQuery Validation, but I wanted to take a different approach to validation. Current approaches deal with writing Javascript code to perform validati开发者_运维问答on on form elements. By looking at the form source code, it is not immediately evident what validation occurs on each element. To some extent this can be remedied by using CSS classes that specify different kinds of validation. But I felt that even this was limited because you cannot easily customize the behavior of the validaton (error messages, etc.). I wanted to do something like annotation-based validation in Java using JSR-303 Bean Validation or Hibernate Validator.

Since HTML5 allows you to add custom attributes to an elements, I figured that I could leverage this to "annotate" form elements for validation. So, essentially, I came up with this:

<input id = "myInput"
       name = "myInput"
       type = "text"
       class = "regula-validation"
       data-constraints = '@NotEmpty @IsNumeric @Between(min=1, max=5)' />

With this basic idea in mind, I created a Javascript framework that:

  • Examines the DOM for elements that have constraints defined and binds these constraints to the elements
  • Allows the creation of custom constraints
  • Allows programmatic binding of constraints
  • Validates bound constraints

In addition, the framework has the following features:

  • Validation groups, similar to that specified in JSR-303
  • Interpolation for error messages

Once I created my framework I tried to get feedback and review for it but I wasn't sure where to go to get feedback and review. I wrote a few blog posts about it and posted it to Digg and Reddit (programming section) without much luck. A few people seemed interested, but I didn't get much more than that.

Recently, at my workplace we've been modernizing a legacy codebase (JSP and servlets) and moving it into Spring MVC. When the conversation of validation came up, I pitched my framework to my senior architect. I did a little integration and proof of concept and they seemed interested and gave me the go-ahead to add it to the project. Till now, I only had my own humble opinion that this would be a helpful way to do validation, so this gave me some confidence that my idea and framework might have some merit. However, I still needed some more participation and framework. After I figured out that Stackoverflow does allow these kinds of questions, I decided to post it on here to get some constructive criticism, comments, and feedback.

So without any more delay, I'd like to introduce Regula. The link I have provided goes to a wiki on GitHub that has all the documentation for the framework. You can download the latest version (v1.1.0) from here.

Looking forward to your comments.

Some extra information that is not immediately relevant

I had toyed with the idea of integrating my framework with Spring, i.e., translating the validation annotations on beans into client-side validation. Recently I was able to get this to work, even with validation groups (although there is no support currently for inheritance relations between the groups on the client side). This way, you simply have to annotate field properties with the validation constraints, and client-side validation-code is automatically generated. However, I am a Spring novice and so my method is probably not that clean. I would like to get some feedback on this as well, so if anyone is interested please let me know. Ideally (and I hope I am not being too pretentious) I would like to contact the Spring folks and see if they are interested in this.


I like it a lot already, it keeps my html clean and the ability to build custom validators is great. One thing I added was a short hand for binding the validation and submit functions, and wrapped it up as a jQuery plugin:

if (jQuery) {
    (function($)
    {
        $.regula = function(formId, callback) 
        {
            regula.bind();

            $("#" + formId).submit(function() 
            {
                var validationResults = regula.validate();

                if (validationResults.length > 0)
                {
                    if (callback)
                        callback(validationResults);

                    return false;
                }

                return true;
            });
        };
    })(jQuery);
}

Infact, I've just blogged about it to as I am that impressed with how clean and easy it is. I'm still going to spend time going through your source, to see how your accomplished it, but its a great start :)

In regards to integrating your framework, I work mostly with ASP.NET MVC, and it would be interesting to see how it translate server-side validation logic into client-side constraints. Something I might look over in the next month or so.


I'm doing completely different approach: With modern framework like React or Angular you have always state of form somewhere in javascript not in DOM input states (DOM is just view layer of data). And I think it should be like this because no matter what kind of fancy components you use to build you form there is always underneath object that holds all states. From this point to me natural approach is to just use raw JSR-303 (without annotations) to validate this object (because JSR-303 provides you such flexibility) and populate errors back to DOM. Let me show you an example:

import React, { Component } from "react";
import ReactDOM from "react-dom";

import validator, {
  Collection,
  All,
  Required,
  Optional,
  NotBlank,
  Length,
  Email
} from "@stopsopa/validator";

class App extends Component {
  constructor(...args) {
    super(...args);
    this.state = {
      data: {
        name: "",
        email: "",
        comments: []
      },
      errors: {},
      validate: false
    };
  }
  onSubmit = async e => {
    e.preventDefault();

    const errors = await validator(
      this.state.data,
      new Collection({
        name: new Required([new NotBlank(), new Length({ min: 3 })]),
        email: new Required([new NotBlank(), new Email()]),
        comments: new All([new NotBlank(), new Length({ min: 10 })])
      })
    );
    this.setState({
      errors: errors.getTree(),
      validate: true
    });

    if (!errors.count()) {
      console.log("send data to server", this.state.data);
    }
  };
  onChange = (name, value) => {
    console.log(name, value);
    this.setState(state => ({
      ...state,
      data: { ...state.data, ...{ [name]: value } }
    }));
  };
  addComment = () =>
    this.setState(state => {
      const comments = state.data.comments;
      comments.push("");
      const newState = { ...state };
      newState.data.comments = comments;
      return newState;
    });
  deleteComment = i =>
    this.setState(state => {
      const newState = { ...state };
      state.data.comments.splice(i, 1);
      return newState;
    });
  editComment = (i, value) => {
    this.setState(state => {
      const newState = { ...state };
      state.data.comments[i] = value;
      return newState;
    });
  };
  render() {
    const s = this.state;
    console.log("state", JSON.stringify(s, null, 4));
    return (
      <form onSubmit={this.onSubmit}>
        <label>
          name:
          <input
            value={s.data.name}
            onChange={e => this.onChange("name", e.target.value)}
          />
        </label>
        {s.validate && s.errors.name && (
          <div className="error">{s.errors.name}</div>
        )}
        <br />
        <label>
          email:
          <input
            value={s.data.email}
            onChange={e => this.onChange("email", e.target.value)}
          />
        </label>
        {s.validate && s.errors.email && (
          <div className="error">{s.errors.email}</div>
        )}
        <div>
          comments:{" "}
          <a onClick={this.addComment} href="javascript:void(0)">
            add
          </a>
          {s.data.comments.map((m, i) => (
            <div style={{ border: "1px solid red" }} key={i}>
              <textarea
                rows="2"
                value={m}
                onChange={e => this.editComment(i, e.target.value)}
              />
              <a
                onClick={() => this.deleteComment(i)}
                href="javascript:void(0)"
              >
                delete
              </a>
              {s.validate && s.errors.comments && s.errors.comments[i] && (
                <div className="error">{s.errors.comments[i]}</div>
              )}
            </div>
          ))}
        </div>
        <br />
        <input type="submit" value="submit" />
      </form>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

life example https://codesandbox.io/s/ymwky9603j

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜