用 TypeScript 写一个轻量级的 UI 框架之七:表单的处理

表单(Form)首先是一些零散的样式、布局 CSS 之类的问题,其次包含表单验证、序列化的功能,最后就是“无刷新”表单(即 AJAX 提交)。至于围绕表单的各种千变万化的控件,如日历控件、HTML 编辑器和文件上传等组件,它们篇幅较大,另文再述。

在线例子:https://framework.ajaxjs.com/demo/form/validator.html

在这里插入图片描述

表单初步

语义化表单

众所周知,HTML 是陈述式(或曰声明式)语言,——尽管恐怕称不上为“语言”。 陈述式语言虽然没有 if...else/for 那些流程语句,但胜在够简单而且清晰明了,我们主张前端能够用陈述式的地方,就尽可能用陈述式。也就是说,推荐使用标签+属性的方式来定义许多的配置,而不是用 JS 声明或定义 UI 对象。

表单亦是如此。举个例子,表单提交的地址,本身就有 action 属性,还有 method 属性等等。我们在 HTML 上面声明了,就不用在 JS 定义。

标签与样式

源码在:https://gitee.com/sp42_admin/ajaxjs/tree/master/aj-ts/src/less/form

表单元素请加入 .aj-form 样式类即为表单里面各控件提供美化过的样式。根据不同的应用场合,表单样式有以下三种结构。

dl/dt/dd 结构

例子就是这种方式。表单元素请加入 .aj-form 样式类即可。结构如下图。

在这里插入图片描述
可见 dl/dt/ddlabel 标签比较繁多,现在已经不推荐使用了。

div 结构

使用 div 来分隔每一行,label 元素来包裹每个控件,比较简单方便。效果如下图所示。

在这里插入图片描述
结构如下图。

在这里插入图片描述注意 form 元素外须有容器且名为 .aj-form-row-holder

table 结构

Table 大法j就是使用传统的 <table> 元素布局,适合复杂且要求对齐的情况,或者后台下不需要移动布局的界面。

简单用法

对于表单,可直接使用 aj.xhr.form(formEl) 绑定表单实现 AJAX 提交。该方法自动绑定表单,url 地址取决于 Form 的 action 元素,方法取决于 method 元素(这是尊重 HTML 标签的体现)。因为都在 HTML 上面配置,故 JS 就是调用一下初始化,最简单的就是:

aj.xhr.form(".my-form");

// 或者自定义提交后的回调
aj.xhr.form(document.querySelector('form'), function(json) {
    if(json.isOk)
        location.assign(json.newlyId);
});

完整方法签名是:

/**
 * 初始化 AJAX 表单
 * 
 * @param form  表单元素,可以是 CSS 选择符,或者是 HTML 元素
 * @param cb    回调函数
 * @param cfg   表单请求的配置参数,可选的
 */
export function form(form: cssSelector | HTMLFormElement, cb: XHR_Callback = defaultCallBack, cfg: XhrFormConfig = {}): void;

配置参数结构如下。

/**
 * 表单请求的配置参数
 */
export interface XhrFormConfig extends XHR_Config {
    /**
     * 表单中要忽略的字段
     */
    ignoreField?: String;

    /**
     * true 表示为不进行表单验证
     */
    noFormValid?: boolean;

    /**
     * true 表示为启用 Google 防注册机验证
     */
    googleReCAPTCHA?: boolean;

    /**
     * 提交之前的触发的事件。
     * 如果返回 fasle 表示阻止提交表单
     */
    beforeSubmit?: (form: HTMLFormElement, json: StringJsonParam) => boolean;
    
}

“无刷新”表单

我们做的第一件事情就是令原本页面刷新提交的表单变成不会刷新的表单,在背后通过 JavaScript 来提交表单数据。禁止浏览器默认情况很简单,就是在表单的 submit 事件里面调用 event.preventDefault(); 即可阻止表单提交。然后在 submit 事件里面自定义的防注册机的校验、表单验证、序列化表单数据和最后的 AJAX 数据提交。这过程的源码我们截图如下图。

在这里插入图片描述在这里插入图片描述

源码还是非常简单的。鉴于和 XHR 的亲缘性,故该部分源码放在 XHR 模块中,详见 https://gitee.com/sp42_admin/ajaxjs/blob/master/aj-ts/src/base/xhr.ts 约第172 行。

表单提交前,你可以加入的事件拦截器,如下例:

aj.xhr.form(document.querySelector('form'), cb, {
	// 加入提交前的拦截器
    beforeSubmit : function(form, json) {... return true/fasle;}, // 返回 false 阻止提交表单
    ignoreField: "foo" // 指定 ignoreField 忽略某个字段。
});

虽然使用了 FormData 收集表单数据,但是 POSTPUT 都是一般的 x-www-form-urlencoded 表单请求,而不是 H5 的 FormData(文件上传的那种)。提交的时候会进行 encodeURIComponent() 编码。

序列化表单数据

所谓表单数据其实就是一个键/值对的集合,序列化下来通常转化为类似 FNAME=Freddie%20Chimp&EMAIL=freddie@mailchimp.com 的字符串提交(multipart/form-data),另外常见的是 POST JSON As Raw Body,我们先说说前者的实现。

为获取表单数据,H5 浏览器直接提供了 FormData 的 API,在此基础上加入我们的一些逻辑,例如拦截某些字段加入(配置 XhrFormConfig)。最终转化为 JSON 提交表单。

/**
 * 表单序列化,返回 JSON
 * 
 * @param form  表单元素
 * @param cfg   是否有忽略的字段
 * @returns Json 参数,已 encodeURIComponent 编码 value
 */
function serializeForm(form: HTMLFormElement, cfg: XhrFormConfig): StringJsonParam {
    let json: StringJsonParam = {}, formData: FormData = new FormData(form);

    formData.forEach((value: FormDataEntryValue, name: string): void => {
        if (cfg && cfg.ignoreField != name) // 忽略的字段
            json[name] = encodeURIComponent(value.toString());
    });

    return json;
}

在前端的都是字符串,即使你是 true/数字/null 的类型,故提供一个 StringJsonParam 的类型,即 key 和 value 皆是 string 的 JSON。要得到数据原本形态的类型(指 true/数字/null 那些,而无须加双引号表示字符串),在后端处理。

技巧两则

按钮支持

针对下面两点进行了封装:

  • 返回按钮,若有 button.returnBtn 的按钮自动加上返回的事件。
  • 底部按钮样式,加入 div.aj-btnsHolder 的区域自动居中/美化按钮。

防注册机

注册机采用谷歌的方案,有开放国内可访问的服务,请自行搜索了解。当然你可以配置 XhrFormConfig.googleReCAPTCHA = false 关闭注册机加载。

表单验证

表单验证我过去也封装过几次,感觉都不是很好,不是效果做不出来,而是通用性不是很好,又复杂。HTML5 时代表单自带验证功能了,这点很好,简单调用 API 就可以了。

首先是检查浏览器是否支持 HTML5 的 validity:

if (!('validity' in document.createElement('input')))
    window.alert("不支持 HTML5 表单验证");

不支持,也没关系,参考一个 polyfill

Polyfill 代码我复制了一份在我的代码中,感觉还用不上,浏览器支持足够好,先注释了。

使用 Validator

单独使用 Validator 的情况很少(如 new aj.form.Validator(document.querySelector("form"));),更多的是集成到 aj.xhr.form(".my-form"); 使用。当然你可以配置 XhrFormConfig.noFormValid = true 关闭 Validator 验证。

源码在:https://gitee.com/sp42_admin/ajaxjs/blob/master/aj-ts/src/form/validator.ts

最后再次提醒,前端进行的表单验证是可以被绕过的,因此你需要在服务器端再次进行验证。

Validator 原理

H5 浏览器都自带有表单验证功能,但我们需要自定义自己的 UI 交互,故只会部分地应用到 H5 表单验证功能。主要围绕字段的 ValidityState 所提供的信息,判别是否合法字段,如不是,提供自定义的 UI 反馈。

验证有以下两个步骤。

  • 当用户离开一个字段时,对该字段进行验证。通过 blur 事件处理。
  • 在提交表单时检查所有的字段。当用户提交表单时, 我们要对表单里的每一个字段进行验证,并且在不合法的字段下面显示错误信息。

其他考量

用户体验的优化是无止境的。本文不打算在这方面太深入细节。当然了有些基础性的用户体验增强是可以做的,例如 loading 提示、loading 按钮禁止状态等,我这里就暂时没有做。假以时日,有空余时间再补上。

参考

  • https://www.w3cplus.com/css/form-validation-part-2-the-constraint-validation-api-javascript.html
  • https://github.com/cferdinandi/validate/blob/master/src/js/validate.js
相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页