【问题标题】:Javascript form-validation framework: Request for ReviewJavascript 表单验证框架:请求审查
【发布时间】:2011-03-29 05:31:27
【问题描述】:

我不确定我是否可以问这种问题,但是在 Meta Stackoverflow 上看到this 之后,这种问题看起来是可以的。好吧,关于我的问题:

几个月前,我用 Javascript 编写了一个验证框架。我知道已经存在像jQuery Validation 这样的验证框架,但我想采用不同的方法进行验证。当前的方法处理编写 Javascript 代码以对表单元素执行验证。通过查看表单源代码,并不能立即看出每个元素上发生了什么验证。在某种程度上,这可以通过使用指定不同类型验证的 CSS 类来解决。但我觉得即使这也是有限的,因为您无法轻松自定义验证的行为(错误消息等)。我想在 Java 中使用 JSR-303 Bean ValidationHibernate Validator 做一些基于注释的验证。

由于 HTML5 允许您向元素添加自定义属性,我想我可以利用它来“注释”表单元素以进行验证。所以,基本上,我想出了这个:

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

考虑到这个基本想法,我创建了一个 Javascript 框架:

  • 检查 DOM 中已定义约束的元素并将这些约束绑定到元素
  • 允许创建自定义约束
  • 允许以编程方式绑定约束
  • 验证绑定约束

此外,该框架还具有以下特点:

  • 验证组,类似于 JSR-303 中指定的组
  • 错误消息的插值

创建框架后,我尝试获取反馈并对其进行审核,但我不确定从何处获取反馈和审核。我写了几篇关于它的博客文章并将其发布到 Digg 和 Reddit(编程部分),但运气不佳。一些人似乎感兴趣,但我没有得到更多。

最近,在我的工作场所,我们一直在对遗留代码库(JSP 和 servlet)进行现代化改造,并将其转移到 Spring MVC 中。当谈到验证时,我将我的框架介绍给了我的高级架构师。我做了一些集成和概念验证,他们似乎很感兴趣,并允许我将其添加到项目中。到目前为止,我只有自己的拙见,认为这将是一种有用的验证方式,所以这让我有信心相信我的想法和框架可能有一些优点。但是,我仍然需要更多的参与和框架。在我发现 Stackoverflow 确实允许这类问题后,我决定将其发布在此处以获得一些建设性的批评、cmets 和反馈。

所以,我想立即介绍Regula。我提供的链接转到 GitHub 上的 wiki,该 wiki 包含该框架的所有文档。您可以从here下载最新版本(v1.1.0)。

期待你们的cmets。

一些不直接相关的额外信息

我曾考虑过将我的框架与 Spring 集成的想法,即将 bean 上的验证注释转换为客户端验证。最近我能够让它工作,即使是验证组(尽管目前不支持客户端组之间的继承关系)。这样,您只需使用验证约束来注释字段属性,客户端验证代码就会自动生成。但是,我是 Spring 新手,所以我的方法可能不是那么干净。我也想得到一些关于这方面的反馈,所以如果有人感兴趣,请告诉我。理想情况下(我希望我不会太自命不凡)我想联系 Spring 人员,看看他们是否对此感兴趣。

【问题讨论】:

  • 我并没有真正使用它的用例;但是,我会投票并喜欢,因为这似乎是一个不错的概念。我很好奇人们是怎么理解它的。我选择的 web 框架 wicket 主要消除了对 javascript 验证的需要,因为它提供了提交数据和通过 ajax 请求从服务器产生反馈的能力。我知道其他框架,如 ASP.NET,确实鼓励客户端验证动态反馈(尽管您总是需要在提交表单时进行服务器验证)。
  • @RMorrisey 感谢您的评论!我还没有尝试过检票口,但我想找个时间看看。我认为 JSF 也做了类似于你描述的事情。在工作中,我们有一些仅在服务器端完成的验证。使用我的框架,我已经能够像您描述的那样在客户端进行操作。我有一个带有验证器的自定义约束,它发出 AJAX 请求来执行验证。再次感谢您的反馈!
  • 我知道这不是主题和时间,但是您是如何将注释注入数据约束的?我在寻找如何做类似的事情时遇到了这个问题(regula 足以满足我的需要)

标签: javascript validation client-side-validation


【解决方案1】:

我已经非常喜欢它了,它使我的 html 保持干净并且构建自定义验证器的能力很棒。我添加的一件事是绑定验证和提交函数的简写,并将其包装为 jQuery 插件:

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);
}

事实上,我刚刚blogged 对此感到满意,因为它的干净和简单给我留下了深刻的印象。我仍然会花时间浏览你的源代码,看看你是如何完成它的,但这是一个很好的开始:)

关于集成您的框架,我主要使用 ASP.NET MVC,看看它如何将服务器端验证逻辑转换为客户端约束会很有趣。我可能会在下个月左右查看一些内容。

【讨论】:

  • 感谢您的反馈和 Matthew 的博文!我主要从事 Java 方面的工作。我从未做过 .NET,但我有兴趣研究它!
  • 我一直在使用它,并找到了一种基于 .NET 的 DataAnnotations 命名空间提供的属性自动生成数据约束的方法。我知道你是一个 Java 小伙子,但如果你有兴趣了解我是如何做到的,今晚我将在我的博客上更新一个示例。我想在春天用 Beans 做这件事的概念基本上是相似的,尽管有 Java 的味道......
【解决方案2】:

我正在做完全不同的方法: 使用像 React 或 Angular 这样的现代框架,你总是在 javascript 中的某个地方而不是 DOM 输入状态中的表单状态(DOM 只是数据的视图层)。而且我认为它应该是这样的,因为无论您使用哪种花哨的组件来构建您的表单,都始终在包含所有状态的对象下方。从这一点对我来说,自然的方法是只使用原始 JSR-303(没有注释)来验证这个对象(因为 JSR-303 为您提供了这种灵活性)并将错误填充回 DOM。让我给你举个例子:

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

生活例子https://codesandbox.io/s/ymwky9603j

【讨论】:

    猜你喜欢
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 2011-10-08
    • 1970-01-01
    • 2017-10-18
    • 2015-12-07
    相关资源
    最近更新 更多