优雅的写表单校验 -- 策略模式的运用

简介: 优雅的写表单校验 -- 策略模式的运用

优雅的写表单校验 -- 策略模式的运用


假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑。

  • 用户名不能为空。
  • 密码长度不能少于 6 位。
  • 手机号码必须符合格式。
<form action="http:// xxx.com/register" id="registerForm" method="post">
  <div>请输入用户名:<input type="text" name="userName" /></div>
  <div>请输入密码:<input type="text" name="password" /></div>
  <div>请输入手机号码:<input type="text" name="phoneNumber" /></div>
  <button>提交</button>
</form>

html 的部分很简单,主要想想提交里怎么写?

来,简单构思下~

来,简单构思下~

来,简单构思下~

凭直觉写的表单校验 - v1

let form = document.querySelector("#registerForm");
form.onsubmit = () => {
  if (form.userName.value === "") {
    alert("用户名不能为空");
    return false;
  }
  if (form.password.value.length < 6) {
    alert("密码不能少于6位");
    return false;
  }
  if (!/[0-9]{11,11}/.test(form.phoneNumber.value)) {
    alert("手机号码格式不正确");
    return false;
  }
};

一顿操作猛如虎,好,写完之后,看看代码有何不妥:

  • registerForm.onsubmit 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有 的校验规则。
  • registerForm.onsubmit 函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长 度校验从 6 改成 8,我们都必须深入 registerForm.onsubmit 函数的内部实现,这是违反开 放—封闭原则的。
  • 算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的 校验,那我们很可能将这些校验逻辑复制得漫天遍野。

联系之前说过的策略模式,来改进代码~

改进版的表单校验 - v2

// 将算法的实现单独封装起来
var strategies = {
  isNonEmpty: function(value, errorMsg) {
    // 不为空
    if (value === "") {
      return errorMsg;
    }
  },
  // 这里把length作为参数
  minLength: function(value, length, errorMsg) {
    // 限制最小长度
    if (value.length < length) {
      return errorMsg;
    }
  },
  isMobile: function(value, errorMsg) {
    // 手机号码格式
    if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
  }
};
// 算法的使用
let form = document.querySelector("#registerForm");
form.onsubmit = function() {
  var errorMsg = "用户名不能为空";
  if ((strategies.isNonEmpty(registerForm.userName.value), errorMsg)) {
    alert(errorMsg);
    return false;
  }
  var errorMsg = "密码长度不能少于6 位";
  if ((strategies.minLength(registerForm.password.value), 6, errorMsg)) {
    alert(errorMsg);
    return false;
  }
  var errorMsg = "手机号码格式不正确";
  if ((strategies.isMobile(registerForm.phoneNumber.value), errorMsg)) {
    alert(errorMsg);
    return false;
  }
  // 这里开始ajax请求。。。
};

写完之后,我屮艸芔茻!!!这好像和第一版也没什么大的区别。看起来一点也不优雅!!!

但这里提供了关键性思路,提交部分明显就是重复,很容易想到列表循环,这里将其抽象成 validator 的类。

也就是 validator 类提供 add 和 start,,add 负责将每种校验方式放进一个数组,start 负责遍历数组,一旦有返回值直接返回且终止返回。

。。。咳咳,有点难度,可以试试写写。

初见美好 - v3

// var strategies = 、、、
class Validator {
  constructor() {
    this.cache = [];
  }
  add(dom, rule, msg) {
    // 把单个验证表单的fn存下来
    let [strategy, length] = rule.split(":");
    let params = length ? [dom.value, length, msg] : [dom.value, msg];
    console.log(params);
    let fn = () => {
      return strategies[strategy](...params);
    };
    this.cache.push(fn);
  }
  start() {
    for (var i = 0; i < this.cache.length; i++) {
      let errMsg = this.cache[i]();
      if (errMsg) {
        return errMsg;
      }
    }
  }
}
// 应用
let form = document.querySelector("#registerForm");
function validateData() {
  let validator = new Validator();
  validator.add(form.userName, "isNonEmpty", "用户名不能为空");
  validator.add(form.password, "minLength:6", "密码不能少于6位");
  validator.add(form.phoneNumber, "isMobile", "手机号码格式不正确");
  // 需要加新的校验规则,直接添加这里就好
  let errMsg = validator.start();
  return errMsg;
}
// submit里面就很优雅了
form.onsubmit = () => {
  let errMsg = validateData();
  if (errMsg) {
    alert(errMsg);
    return false;
  }
};

写完这些之后,很赞啦!

但。。。还有点点缺点,比如用户名想同时验证其为空和长度的话,需要单个列出,显然又累赘了

接下来试着将其改成validator.add(form.userName, {isNonEmpty:'用户名不能为空','minLength:6':'用户名长度不能少于6位'})

validator.add(form.userName, "isNonEmpty", "用户名不能为空");
validator.add(form.userName, "minLength:6", "用户名长度不能少于6位");

相知相识 - v4

// var strategies = 。。。;
class Validator {
  constructor() {
    this.cache = [];
  }
  // 没事原先的add提取出来
  _handleRule(dom, rule, msg) {
    // 把单个验证表单的fn存下来
    let [strategy, length] = rule.split(":");
    let params = length ? [dom.value, length, msg] : [dom.value, msg];
    console.log(params);
    let fn = () => {
      return strategies[strategy](...params);
    };
    this.cache.push(fn);
  }
  add(dom, rules) {
    rules.forEach(item => {
      for (let rule in item) {
        let msg = item[rule];
        // 遍历的时候,直接使用就好
        this._handleRule(dom, rule, msg);
      }
    });
  }
  start() {
    for (var i = 0; i < this.cache.length; i++) {
      let errMsg = this.cache[i]();
      if (errMsg) {
        return errMsg;
      }
    }
  }
}
// 应用
let form = document.querySelector("#registerForm");
function validateData() {
  let validator = new Validator();
  validator.add(form.userName, [
    { isNonEmpty: "用户名不能为空" },
    { "minLength:6": "用户名不能少于6位" }
  ]);
  validator.add(form.password, [{ "minLength:6": "密码不能少于6位" }]);
  validator.add(form.phoneNumber, [{ isMobile: "手机号码格式不正确" }]);
  console.log(validator);
  let errMsg = validator.start();
  return errMsg;
}
form.onsubmit = () => {
  let errMsg = validateData();
  if (errMsg) {
    alert(errMsg);
    return false;
  }
};

完结,下次看表单验证的一些插件可能就能加深理解了!

引用

这里的案例是《JavaScript的设计模式与开发实践》里面的,写的非常好,强烈安利!!!

对了之前写了个策略模式的入门

目录
相关文章
|
10月前
|
设计模式 算法 PHP
php设计模式--策略模式(六)
php设计模式--策略模式(六)
56 0
|
10月前
|
设计模式 JavaScript 开发者
Vue的混入(Mixins):混入的使用和设计模式解析
【4月更文挑战第24天】Vue Mixins是实现组件复用的灵活工具,允许共享可复用功能。混入对象包含组件选项,如数据、方法和生命周期钩子,可被合并到使用它的组件中。通过组合模式和钩子注入模式,混入能提高代码复用和可维护性。然而,注意命名冲突、选项合并策略以及慎用全局混入以防止副作用。正确使用混入能提升开发效率和软件质量。
element表单组件的trigger表单验证逻辑规则
校验元素trigger属性值应该怎么选择?
394 0
|
设计模式 JavaScript 前端开发
设计模式 - 混入模式(Mixin Pattern)
在JavaScript中,Mixin Pattern(混入模式)是一种强大的设计模式,它允许开发者在类之间共享方法和属性,实现代码的复用和模块化。Mixin模式可以让我们轻松地扩展一个类,同时保持代码的简洁和可维护性。本文将从浅入深,介绍如何使用JavaScript实现Mixin Pattern。
380 0
|
存储 算法 Java
行为型模式 - 模板模式(Template Pattern)
行为型模式 - 模板模式(Template Pattern)
|
设计模式 C++
用设计模式去掉没必要的状态变量 —— 状态模式
这是在UI开发中经常会遇到的场景:界面有两种状态,每一种状态下界面元素对应的操作都不同。比如在 offline 状态下点击大叉会直接退出应用,而在 login 状态下点击大叉会退出登录。 最简单直观的
112 0
|
设计模式 算法 JavaScript
「手摸手设计模式系列」 策略模式与动态表单验证
策略模式 (Strategy Pattern)又称政策模式,其定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。封装的策略算法一般是独立的,策略模式根据输入来调整采用哪个算法。关键是策略的实现和使用分离。
「手摸手设计模式系列」 策略模式与动态表单验证
|
设计模式 算法 Java
行为型-Template
模板模式的原理与实现 模板模式,全称是模板方法设计模式,英文是 Template Method Design Pattern。在 GoF 的《设计模式》一书中,它是这么定义的
123 0
|
设计模式
Head First 设计模式 —— 03. 装饰器 (Decorator) 模式
Head First 设计模式 —— 03. 装饰器 (Decorator) 模式
359 0
|
JavaScript 数据安全/隐私保护