Ajax:表单 & 模板引擎
- form 表单
- form 属性
- Ajax操控表单
- 事件监听
- 阻止默认行为
- 收集表单数据
- 模板引擎
- art-template
- {{}}语法
- 原文输出
- 条件输出
- 循环输出
- 过滤器
- 原理
form 表单
在HTML
中,可以通过<form>
创建一个表单,收集用户信息。而采集到的信息想要发送给后端,此时就要与Ajax
进行配合。
form 属性
在原生HMTL
表单中,包含四个基本属性:
属性 | 功能 | 值 |
---|---|---|
action | 向何处提交表单内的数据 | url |
method | 提交表单的方式 | get 、post |
enctype | 如何对表单数据编码 | |
target | 打开action url 的为止 | _blank 、_self |
- action
action
的属性值往往是后端提供的一个url
地址,这个地址接收并处理来自前端的数据。如果<form>
没有指定该值,那么默认为当前页面的url
。
示例:
<form action="/login">
<input type="account" name="account" />
<input type="password" name="password" />
<button type="submit">提交</button>
</form>
这是一个表单,指定action="/login"
。
输出结果:
可以看到,点击提交后,地址变成了/login?account=123456&password=54321
,其中/login
就是表单提交到的地址,而?
后面是表单的参数。
这里有两个细节:
- 目标地址默认是在当前页面打开
- 表单的参数追加到了地址的末尾,说明默认是
get
请求
- taget
taget
指定在何处打开地址,刚刚打开地址是,是默认在当前页面打开,通过指定target
就可以改变打开目标地址的方式。
_blank
:在新窗口打开_self
:在当前窗口打开,默认值
- method
刚刚发现,表单的提交方式默认为get
,如果想要切换表单提交方式,可以通过method
属性。
get
:以get形式发送请求,默认值post
:以post形式发送请求
示例:
<body>
<form action="/login" method="post" >
<input type="account" name="account" />
<input type="password" name="password" />
<button type="submit">提交</button>
</form>
</body>
此处把表单的提交方式改为post
。
提交表单后,查看控制台:
这次提交数据后,地址就是/login
,表单的数据不再以?
的形式追加到地址末尾。而在Payload
窗口,可以看到表单数据account
和password
已经放在了请求的内部。
- enctype
enctype
用于指定表单数据的编码方式,其有三个可选值:
application/x-www-form-urlencoded
:在发送数据前编码所以的字符,默认值multipart/form-data
:不对字符编码,如果表单内上传文件时,必须使用该选项,防止错误编码text/plain
:空格转换为'+'
字符,但是不对特殊字符编码,几乎不用
原生的HTML
表单有很多缺点:
- 表单提交后,会发生页面跳转
- 表单提交后,会清空表单原有数据
想要处理这些问题,那就要引入JavaScript
和Ajax
,JavaScript
可以阻止默认行为,禁止页面进行跳转。但是这样也会阻止数据的提交,因此使用Ajax
来进行表单数据提交,不再让表单自己提交数据。而且Ajax
提交数据,不会清空表单的内容。
Ajax操控表单
事件监听
如果要在用户提交表单时,让Ajax
立刻响应,代替表单的提交任务,此时就要对表单进行提交事件的事件监听。
只需要调用原先的jQuery
接口即可:
$('form').submit(function(){})
$('form').on('submit', function(){})
以上两种方式,都可以实现对表单的提交事件的监听。
阻止默认行为
想要阻止默认提交行为,也很简单:
$('form').on('submit', function (e) {
e.preventDefault()
})
在回调函数中,添加是事件参数e
,随后调用preventDefault
方法阻止默认行为,这可以同时阻止提交行为和页面跳转。
收集表单数据
现在已经阻止了提交行为,接下来就要通过Ajax
代替原先的提交行为了。在提交数据之前,就要先获取到表单的数据,这里jQuery
封装了接口,可以一次性获取所有表单内容。
语法:
$('form').serialize()
注意:使用这个方法时,要确保表单的每个栏目都有name
属性,否则无法获取到对应的栏目。
示例:
<form action="/login" id="f1">
<input type="account" name="account" />
<input type="password" name="password" />
<button type="submit">提交</button>
</form>
<script>
$(function () {
$('#f1').on('submit', function (e) {
e.preventDefault()
var data = $(this).serialize()
console.log(data)
})
})
</script>
输出结果:
此时data
就已经拿到了表单的内容,最后就可以通过Ajax
进行发送了。
模板引擎
当从表单中拿到了大量的数据,此时可能就需要把这些数据重新渲染到页面上,如果对于原生的JavaScript
或者jQuery
,处理这个都是比较麻烦的,需要进行字符串拼接等操作,把数据拼接到标签的内部。
假设现在要渲染以下内容:
var data = {
title: '<h3>用户信息</h3>',
name: '张三',
age: 18,
isVIP: true,
regTime: new Date(),
hobby: ['HTML', 'HTTP', 'Linux']
}
渲染为如下结构:
如果只使用html
和jQuery
,那么就要从对象提取信息,然后一个一个通过.html
方法把信息添加到页面中,对于最后一个hobby
数组,还需要通过.each
方法进行遍历,追加到页面中。
art-template
这很麻烦,模板引擎可以解决这个问题,以模板的形式,快速渲染大量的信息到网页。此处选用art-template
模板引擎,可以到以下网址下载:
http://aui.github.io/art-template/zh-cn/docs/installation.html
只需要把template-web.js(gzip: 6kb)
这个文件下载下来,并通过JavaScript
的形式引入即可使用。
<script src="./template-web.js"></script>
导入art-template
后,全局就会多出一个template
方法,这就是模板引擎的核心方法。
示例:
首先,模板引擎的内容要定义在<script>
标签内部:
<script type="text/html" id="tpl">
<p>姓名: {{name}}</p>
<p>年龄: {{age}}</p>
</script>
此处要制定type
属性和id
属性,默认情况下type="text/JavaScript"
,表示以JavaScript
形式解析<script>
内部的内容,此处改为text/html
,表示以html
形式解析内容。
随后就可以在内部编写html
的代码了,在编写代码时,可以使用{变量名}}
的形式来引入外部变量,这个变量会直接取对象中的值进行填入。
模板语法:
template('id', 对象)
此处的id
是刚才<script>
内部的属性id="tpl"
,而对象是要填充的对象,最后会返回格式化后的html
字符串。
示例:
<script>
var data = { name: '张三', age: 20}
var htmlStr = template('tpl-user', data)
$('#container').html(htmlStr)
</script>
在对象data
中,包含name
和age
两个属性,这刚好和之前指定的{{name}}
和{{age}}
相匹配。随后调用template('tpl, data)
,表示用data
这个对象值,去填充模板tpl
的内容,此时就会返回一个html
格式的字符串,直接把字符串填入目标容器即可。
<div id="container"></div>
<script type="text/html" id="tpl-user">
<p>姓名: {{name}}</p>
<p>年龄: {{age}}</p>
</script>
<script>
var data = { name: '张三', age: 20}
var htmlStr = template('tpl-user', data)
$('#container').html(htmlStr)
</script>
输出结果:
这样就实现了快速渲染对象的内容到html
中。
{{}}语法
刚才已经见过{{}}
的基本功能了,其实就是把对象中命名相匹配的内容,直接填充到html
中,其实它还支持很多种语法格式:
{{value}}
{{obj.key}} // 获取对象值
{{obj['key']}} // 获取对象值
{{a ? b : c}} // 三元表达式
{{a || b}} // 逻辑判断
{{a + b}} // 算数运算
原文输出
如果输出的内容是一个字符串,并且字符串中包含html
标签,如果希望这个标签被渲染,需要使用原文输出
:
{{@ value}}
在值前面加一个@
符号即可。
示例:
<div id="container"></div>
<script type="text/html" id="tpl">
<p>姓名: {{@ name}}</p>
<p>年龄: {{age}}</p>
</script>
<script>
var data = { name: '<li>张三</li>', age: 20}
var htmlStr = template('tpl', data)
$('#container').html(htmlStr)
</script>
输出结果:
通过{{@ name}}
,<li>
标签就被正常解析了。
条件输出
有时候,可能需要在输出时进行条件判断,比如说如果某个用户是VIP
,要额外输出VIP
的剩余时间。而如果他不是VIP
,这个内容就不用输出。对于这种情况,可以使用条件输出
来完成。
语法:
{{if value}} 输出内容 {{/if}}
{{if value}} 输出内容1 {{else}} 输出内容2 {{/if}}
{{if value1}} 输出内容1 {{else if value2}} 输出内容2 {{/if}}
示例:
<script type="text/html" id="tpl">
<p>姓名: {{name}}</p>
<p>年龄: {{age}}</p>
{{if isVip}}
<p> VIP剩余时间: {{time}} </p>
{{else}}
<p> 不是VIP </p>
{{/if}}
<hr>
</script>
在模板中,通过{{if isVip}}
来判断该用户是不是VIP
,并选择不同的输出结果。
填入以下两个对象:
<script>
var zs = {
name: '张三',
age: 20,
isVip: true,
time: 100
}
var ls = {
name: '李四',
age: 25,
isVip: false,
time: null
}
var htmlStr1 = template('tpl', zs)
var htmlStr2 = template('tpl', ls)
$('#container').html(htmlStr1 + htmlStr2)
</script>
示例中,张三
是VIP
,而李四
不是,最后这两者渲染出来的结果就不一样:
循环输出
如果输出的目标是一个数组,可以通过循环输出
来遍历所有的数组元素。
语法:
{{each arr}}
{{$index}} {{$value}}
{{/each}}
其中arr
是被遍历的数组,而$index
是下标,$value
是元素值。
示例:
<script type="text/html" id="tpl">
<p>姓名: {{name}}</p>
<p>年龄: {{age}}</p>
<ul>
{{each hobby}}
<li> {{$value}} </li>
{{/each}}
</ul>
</script>
在<ul>
内部,通过循环遍历所有的hobby
,并通过{{$value}}
拿到元素值,渲染到li
中。
给张三加上hobby
属性:
<script>
var zs = {
name: '张三',
age: 20,
hobby: ['HTML', 'HTTP', 'Linux']
}
var htmlStr = template('tpl', zs)
$('#container').html(htmlStr)
</script>
输出结果:
这样就可以快速遍历所有的数组成员了。
过滤器
如果说,对象中的元素并不是直接填写到页面中的,而是要经过一定的处理之后在填入。比如说商品价格,可能要乘以某个值后,以打折的价格显示在页面中,这就需要引入过滤器
。
{{value | filterName }}
其中value
是要处理的值,这个值会交给filterName
函数处理,函数返回值会最为最终结果填入模板。
过滤器函数并不是直接就可以调用原生的函数,而是要导入到模板中:
template.defaults.imports.filterName = function(value){ 函数体 }
现在张三对象内容如下:
var zs = {
name: '张三',
age: 20,
hobby: ['HTML', 'HTTP', 'Linux'],
startYear: 2023
}
此处的startYear
表示入学年份,最终渲染到页面中时,希望输出学年: 入学年份 ~ 毕业年份
,此时可以交给一个过滤器处理:
template.defaults.imports.graduate = function(value){
return value + " ~ " + (value + 4)
}
过滤器graduate
会进行一个字符串拼接,虽然value
是数字类型,但是中间的" ~ "
是一个字符串,最终整个返回值就是一个字符串,毕业年份是入学年份的四年后。
在模板中,把startYear
输入到过滤器:
<script type="text/html" id="tpl">
<p>姓名: {{name}}</p>
<p>年龄: {{age}}</p>
<ul>
{{each hobby}}
<li> {{$value}} </li>
{{/each}}
</ul>
<p>
学年: {{ startYear | graduate }}
</p>
</script>
此处{{ startYear | graduate }}
就是在处理数据。
输出结果:
原理
其实模板引擎的原理非常简单,就是正则表达式匹配,随后替换元素值即可。
以art-template
为例,其所有占位符格式都是{{}}
,那么正则就可以写为:
let pattern = /{{([a-zA-Z]+)}}/
首先通过最外层的{{}}
进行占位符得到匹配,内层一个()
进行分组匹配,提取出具体的信息,[a-zA-Z]+
表示提取任意多个字母。
比如对于字符串<div>{{name}}</div>
,通过以上正则就可以得到name
这个值。
var str = '<div>{{name}}</div>'
var pattern = /{{([a-zA-Z]+)}}/
var result = pattern.exec(str)
console.log(result)
输出结果:
可以看到,最后正则返回一个数组,第一个元素是匹配成功的字符串,第二个元素是提取出来的分组值。
当提取到变量名后,就要去对象中拿到对于的变量,并替换原先的占位符,替换通过replace
方法完成:
'string'.replace('子串', '替换值')
比如说"helloworld".replace("world", "linux")
,就会返回字符串"hellolinux"
。
let obj = {
name: "张三"
}
var str = '<div>{{name}}</div>'
var pattern = /{{([a-zA-Z]+)}}/
var patternResult = pattern.exec(str)
str = str.replace(patternResult[0], obj[patternResult[1]])
console.log(str)
patternResult[0]
是被匹配的字符串,也就是{{name}}
,patternResult[1]
是提取到的分组,也就是name
。只需要把原字符串str
的{{name}}
替换为obj['name']
即可,也就是str.replace(patternResult[0], obj[patternResult[1]])
。
输出结果:
这样就完成了元素的替换。
当整个字符串中有多个{{xxx}}
,可以使用一个循环进行匹配:
let patternResult = null;
while (patternResult = pattern.exec(str)) {
str = str.replace(patternResult[0], obj[patternResult[1]])
}
当pattern.exec
匹配不到{{}}
时,就会返回一个null
,循环就会结束,整个模板字符串被完全替换为对象的值,最后得到一个可以直接渲染的html
字符串。