Ajax第二天
今日目标
- 能够说出form表单的常用属性
- 能够知道如何阻止表单的默认提交行为
- 能够知道如何使用
jQuery
快速获取表单数据 - 能够知道如何安装和使用模板引擎
- 能够知道模板引擎的实现原理
文章目录
一、Form表单的基本使用
1、什么是表单
表单在网页中主要负责 数据采集功能 。HTML中<form>
标签,就是用于采集用户输入的信息,并通过 <form>
标签的提交操作,把采集的信息提交到服务器端进行处理
2、表单的组成部分
表单由三个基本部分组成:
- 表单标签
- 表单域:包含了文本框,密码框,隐藏域,都行文本框,复选框,单选框,下拉选择框和文件上传框等等
- 表单按钮:通过设置
type
属性为submit
来触发form
表单的提交
3、<form>
标签的属性
<from>
标签用来采集数据,<from>
标签的属性则是用来规定 如何把采集到的数据发送到服务器
属性 | 值 | 描述 |
---|---|---|
action | URL 地址 | 规定当提交表单时,向何处发送表单数据 |
method | get 或者 post | 规定以何种方式把表单数据提交到 action 提供的 URL 地址中 |
enctype | application/x-www-form-urlencoded multipart/form-data text/plain | 规定在发送表单数据之前如何对其进行编码 |
target | _blank _self _parent _top framename | 规定在何处打开 action URL |
-
action
action
属性用来规定当提交表单时,向何处发送表单数据。action
属性的值应该是后端提供的一个URL地址,这个URL地址专门负责接收表单提交过来的数据。当
<form>
表单在未指定action
属性值的情况下,action
的默认值为当前页面的URL
地址注意: 当提交表单后,会立即跳转到
action
属性指定的URL
地址 -
target
target
属性用来规定 在何处打开action URL
它的可选值有5个,默认情况下,target的值是 _self,表示在相同的框架中打开 action URL
值 _blank
提交时在新的窗口打开 _slef
默认值,在本窗口打开 _parent
在父框架集中打开(很少用) _top
在整个窗口中打开(很少用) framename 在指定的框架中打开(很少用) -
method
method
属性用来规定 以何种方式 把表单数据提交到 action URL它的可选值有两个,分别是
get
和post
默认情况下,
method
的值为get
, 表示通过URL
地址的形式,把表单数据提交到action URL
注意:
- get 方式适合用来提交少量的,简单的数据
- post 方式适合用来提交大量的,复杂的,或包含文件上传的数据
-
enctype
enctype
属性用来规定在 发送表单数据之前如何对数据进行编码它的可选值有三个,默认情况下,
enctype
的值为application/x-www-form-urlencoded
,表示在发送前编码的所有字符值 描述 application/x-www-form-urlencoded 在发送前编码所有字符(默认) multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值 text/plain 空格转换为 “+” 加号,但不对特殊字符编码(很少用) 注意:
1、在涉及到 文件上传 的操作时,必须 将 enctype 的值设置为 multipart/form-data
2、如果表单提交不涉及到文件上传操作,则直接将 enctype 的值设置为 application/x-www-form-urlencoded 即可
4、表单的同步提交及缺点
-
什么是表单的同步提交
通过点击 submit 按钮,触发表单提交的操作,从而使页面跳转到
action URL
的行为,叫做表单的同步提交 -
表单同步提交的缺点
<form>
表单同步提交后,整个页面会发生跳转,跳转到 action URL 所指向的地址,用户体验很差<form>
表单同步提交后,页面之前的状态和数据会丢失
-
如何解决
表单只负责采集数据,Ajax负责将数据提交到服务器
二、通过Ajax提交表单数据
1、监听表单提交事件
在 jQuery
中,可以使用如下两种方式,监听到表单的提交事件
代码:
// 第一种方式
$('#f1').submit(function () {
alert('监听到了表单的提交事件')
})
// 第二种方式
$('#f1').on('submit', function () {
alert('监听到了表单的提交事件2')
})
2、阻止表单默认提交行为
当监听到表单的提交事件以后,可以调用事件对象的 event.preventDefault()
函数,来阻止表单的提交和页面的跳转
代码:
// 第一种方式
$('#f1').submit(function (e) {
alert('监听到了表单的提交事件')
e.preventDefault()
})
// 第二种方式
$('#f1').on('submit', function (e) {
alert('监听到了表单的提交事件2')
e.preventDefault()
})
3、如何快速获取表单数据
为了简化表单中数据的获取操作,jQuery
提供了 serialize()
函数
语法:
$(selector).serialize()
好处:可以一次性获取表单的数据
代码:
// html 结构
<form action="/login" id="f1">
<input type="text" name="user_name" />
<input type="password" name="password" />
<button type="submit">提交</button>
</form>
// js
$(function () {
$("#f1").submit(function (e) {
e.preventDefault();
let data = $(this).serialize();
console.log(data);
});
});
注意:在使用 serialize() 函数快速获取表单数据时, 必须为每个表单元素添加 name 属性
三、案例 - 评论列表
-
效果图
-
UI 结构搭建
步骤:
- 评论面板结构
- 创建panel (快捷键:
bs3-panel:primary
) - 修改title里面的标题
- 在body 里面 创建两个输入框,加上对应的文本提示
- 最下面加上一个 button(快捷键:
bs3-button:primary
)
- 创建panel (快捷键:
- 评论列表结构
- 构建一个
list
列表(快捷键:bs3-list-group
) - 在第一个
li
里面放两个span
,写入 评论时间和评论人
- 构建一个
代码:
<!-- 评论面板 --> <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">发表评论</h3> </div> <form class="panel-body" id="formAddCmt"> <div>评论人:</div> <input type="text" class="form-control" name="username" /> <div>评论内容:</div> <textarea class="form-control" name="content"></textarea> <button type="submit" class="btn btn-primary">发表评论</button> </form> </div> <!-- 评论列表 --> <ul class="list-group"> <li class="list-group-item"> <span class="badge" style="background: orange">评论时间:</span> <span class="badge" style="background: skyblue">评论人:</span> Item 1 </li> </ul>
- 评论面板结构
-
获取评论列表数据
步骤:
- 定义函数来获取评论列表数据
getCommentList()
- 查阅接口文档,关注请求
url
,是否需要携带参数,请求方式 - 利用
$.ajax()
来进行请求 - 在
success
回调函数中,判断请求数据是否成功,如果状态码不是200,提示用户
代码:
let baseUrl = `http://www.liulongbin.top:3006`; // 获取评论列表 function getCommentList() { $.ajax({ method: "GET", url: baseUrl + "/api/cmtlist", data: {}, success: function (res) { if (res.status !== 200) { return alert("获取数据失败!"); } console.log(res.data); }); }
- 定义函数来获取评论列表数据
-
渲染评论列表
步骤:
- 创建一个空数组(
rows
),用来存放每一个元素的html
字符串结构 - 遍历服务器返回的数据,每遍历一次,拼接一个对应的
html
字符串结构,然后放入到数组中 - 找到list容器,先清空一下里面内容,然后利用 append 添加新的数据
代码:
// 获取评论列表 function getCommentList() { $.ajax({ method: "GET", url: baseUrl + "/api/cmtlist", data: {}, success: function (res) { if (res.status !== 200) { return alert("获取数据失败!"); } console.log(res.data); let arr = res.data.slice(0, 5); let rows = []; $.each(arr, function (i, item) { rows.push( `<li class="list-group-item"> <span class="badge" style="background: orange;">评论时间:${item.time}</span> <span class="badge" style="background: skyblue;">评论人:${item.username}</span> ${item.content} </li>` ); }); $(".list-group").empty().append(rows.join("")); }, }); }
- 创建一个空数组(
-
发表评论
-
改造form表单
步骤:
- 把之前
panel-body
的标签改成form
标签 - 给每一个输入框设置
name
属性,name属性的值最好与接口文档定义的参数名称一致 - 注册
sumbit
事件,阻止表单提交的默认行为 - 获取用户输入内容(利用
serialize()
)
代码:
// 发布评论 $("#formAddCmt").on("submit", function (e) { e.preventDefault(); let data = $(this).serialize(); });
- 把之前
-
完成发表功能
步骤:
- 查阅接口文档
- 利用
$.post()
发送请求,传入数据 - 在成功回调函数里面判断 返回的
status
是否是201,如果是代表成功,失败进行提示 - 刷新页面(调用
getCommentList()
),清空表单内容($('#formAddCmt')[0].reset()
)
代码:
// 发布评论 $("#formAddCmt").on("submit", function (e) { e.preventDefault(); let data = $(this).serialize(); $.ajax({ method: "POST", url: baseUrl + "/api/addcmt", data, success: (res) => { if (res.status !== 201) { return alert("评论发布失败!"); } getCommentList(); console.log($(this)); $(this)[0].reset(); }, }); });
-
四、模板引擎的基本概念
1、渲染 UI 结构时遇到的问题
以前都是通过 拼接字符串 或是 模版字符串 的形式来渲染 UI 结构,这样操作是比较麻烦的,而且很容易出现问题
如果 UI 结构比较复杂,则拼接字符串的时候需要格外注意 引号之前的嵌套 。且一旦需求发生变化,修改起来也非常麻烦
2、什么是模板引擎
模板引擎,它可以根据程序员指定的 模板结构 和 数据,自动生成一个完整的HTML页面
3、使用模板引擎的好处
- 减少了字符串的拼接操作
- 使代码结构更清晰
- 使代码更易于阅读与维护
五、art-template模板引擎
1、art-template 简介
art-template 是一个简约,超快的模板引擎,中文官首页:http://aui.github.io/art-template/zh-cn/index.html
2、安装 art-template
- 浏览器访问 http://aui.github.io/art-template/zh-cn/docs/installation.html
- 找到 安装 导航栏,找到下载链接,右键下载即可
3、art-template 模板引擎基本使用
-
导入
art-template
- 在window全局,就多了一个函数,叫做 template(‘模板id’,需要渲染的数据对象)
<script src="./lib/template-web.js"></script>
-
定义数据
const data = { name: 'zs', age: 20}
-
定义模板
- 模板的 HTML 结构,必须定义到
script
标签中,注意:需要把type属性改成text/html
- 给 模板 添加一个
id
- 模板里面如果需要使用到传入的数据,利用 {{}} 来实现,例如:{{name}},那么就会去找 我们调用 template() 函数 第二个参数里面对应的
name
属性的值
<script type="text/html" id="tpl-user"> <h1>{{name}} ------ {{age}}</h1> </script>
- 模板的 HTML 结构,必须定义到
-
调用
template
函数- 函数的返回值就是拼接好的模板字符串
const htmlStr = template('tpl-user', data)
-
渲染
HTML
结构- 最后我们需要把template返回的模板字符串设置到页面容器中
$('#container').html(htmlStr)
4、art-template 标准语法
art-template 提供了 {{}} 这种语法格式,在 {{}} 内可以进行 变量输出 或 循环数组 等操作,这种 {{}} 语法在 art-template 中被称为标准语法
-
输出
语法:
{{ value }} {{ obj.key }} {{ obj['key'] }} {{ a ? b : c }} {{ a || b }} {{ a + b }}
在 {{}} 语法中,可以进行 变量 的输出,对象属性 的输出,三元表达式 输出,逻辑或 输出,加减乘除等表达式 输出
-
原文输出
语法:
{{@ value }}
如果输出的 value 值中,包含了 HTML 标签结构,则需要使用 原文输出 语法,才能保证HTML标签被正常渲染
注意:@前面不能有 空格
-
条件输出
如果要实现条件输出,则可以在 {{}} 中使用 if…else if…/if 的方式,进行按需输出
语法:
{{ if flag == 0 }} 当 flag 等于 0 时进行输出 {{ else if flag == 1 }} 当 flag 等于 1 时进行输出 {{ /if }}
-
循环输出
如果要实现循环输出,则可以在{{}} 内,通过 each 语法循环数组,当前循环的索引使用
$index
进行访问,当前循环项使用$value
进行访问语法:
{{ each arr }} {{ $index }} {{ $value }} {{ /each }}
-
过滤器
过滤器本质就是一个
function
函数语法:
{{ value | filterName }}
过滤器语法类似于 管道操作符,它的上一个输出作为下一个输入
定义过滤器的基本语法如下:
template.defaults.imports.filterName = function(value){ return 处理结果 }
-
案例 - 格式化时间过滤器
-
定义数据
const data = { regTime: new Date() }
-
定义过滤器函数
template.defaults.imports.dateFormat = (date) => { const y = date.getFullYear(); const m = date.getMonth() + 1; const d = date.getDate(); return y + "-" + m + "-" + d; };
-
在模板引擎中使用过滤器
<div>注册时间为:{{ regTime | dateFormat }}</div>
-
六、案例 - 新闻列表
-
效果
-
获取新闻列表数据
- 定义函数,获取新闻列表数据
getNewsList()
- 查阅接口文档,关注 请求
url
,请求方式,请求参数,响应数据 - 利用
$.ajax()
发起请求 - 在回调函数中,判断请求是否成功
代码:
// 获取新闻列表 function getNewsList() { $.ajax({ method: "GET", url: "http://www.liulongbin.top:3006/api/news", data: {}, success: function (res) { if (res.status !== 200) { return alert("获取数据失败!"); } console.log(res); }, }); }
- 定义函数,获取新闻列表数据
-
定义新闻列表
item
模板- 创建 script 标签,更改type属性值为
text/html
,给模板定义id - 找到静态页面中 item 的结构,拷贝到模板里面
代码:
<script type="text/html" id="tpl-info"> <div class="news-item"> <img class="thumb" src="" alt="" /> <div class="right-box"> <h1 class="title">5G商用在即,三大运营商营收持续下降</h1> <div class="tags"> <span>三大运营商</span> <span>中国移动</span> <span>5G商用</span> </div> <div class="footer"> <div> <span>胡润百富</span> <span>2019-10-28 10:14:38</span> </div> <span>评论数:66</span> </div> </div> </div> </script>
- 创建 script 标签,更改type属性值为
-
编译模板渲染结构
- 在请求成功的回调里面,先对返回数据进行改造,里面返回的
tags
是一个字符串,我们需要分割成数组 - 调用
template()
方法,传入id
和返回的数据 - 把函数的返回值添加到页面容器中
- 在模板中利用
each
遍历列表 - 利用
$value
能够拿到每一个item
项 - 设置图片
img
,**注意:**需要拼接请求根路径 - 设置标题
title
- 设置标签,注意:标签在之前改造成数组了,这里又需要进行遍历
- 设置来源
source
- 设置时间,注意:时间需要格式化,利用过滤器来实现
- 设置评论数
cmtcount
代码:
<script type="text/html" id="tpl-info"> {{ each data }} <div class="news-item"> <img class="thumb" src="http://www.liulongbin.top:3006{{ $value.img }}" alt="" /> <div class="right-box"> <h1 class="title">{{$value.title}}</h1> <div class="tags"> {{ each $value.tags }} <span>{{ $value }}</span> {{ /each }} </div> <div class="footer"> <div> <span>{{$value.source}}</span> <span>{{$value.time | dateFormat}}</span> </div> <span>评论数:{{$value.cmtcount}}</span> </div> </div> </div> {{ /each }} </script>
- 在请求成功的回调里面,先对返回数据进行改造,里面返回的
-
定义格式化时间过滤器
- 调用
template.defaults.imports
添加dateFormat()
函数
代码:
// 定义格式化时间过滤器 template.defaults.imports.dateFormat = (dateStr) => { const date = new Date(dateStr); const y = padZero(date.getFullYear()); const m = padZero(date.getMonth() + 1); const d = padZero(date.getDate()); const hh = padZero(date.getHours()); const mm = padZero(date.getMinutes()); const ss = padZero(date.getSeconds()); return y + "-" + m + "-" + d + " " + hh + ":" + mm + ":" + ss; }; // 定义补 0 函数 function padZero(n) { if (n >= 10) return n; return "0" + n; }
- 调用
七、模板引擎的实现原理
1、正则与字符串操作
-
exec 函数
exec()
函数用于 检索字符串 中的正在表达式的匹配如果字符串中又匹配的值,则返回该匹配值,否则返回 null
语法:
RegExpObject.exec(string)
代码:
let str = "Hello"; let pattern = /x/; let result = pattern.exec(s); console.log(result);
-
分组
正则表达式中 () 包起来的内容表示一个分组,可以通过分组来 提取自己想要的内容,示例代码如下:
var str = '<div>我是{{name}}</div>' var pattern = /{{([a-zA-Z]+)}}/ var result = pattern.exec(str) console.log(result)
-
字符串的 replace 函数
replace() 函数用于在字符串中 用一些字符 替换 另一些字符的
语法:
var result = "123456".replace('123', 'abc') console.log(result); // abnc456
代码:
let str = "<div>我是{{name}}</div>"; const pattern = /{{([a-zA-Z]+)}}/; const patternResult = pattern.exec(str); const result = str.replace(patternResult[0], patternResult[1]); console.log(result); // <div>我是name</div>
-
多次 replace
代码:
let str = "<div>我是{{ name }},今年{{ age }}岁。</div>"; const pattern = /{{\s*([a-zA-Z]+)\s*}}/; // 第一次匹配 const res1 = pattern.exec(str); str = str.replace(res1[0], res1[1]); // 第二次匹配 const res2 = pattern.exec(str); str = str.replace(res2[0], res2[1]); console.log(str); // <div>我是name,今年age岁。</div> // 第三次匹配 const res3 = pattern.exec(str); console.log(res3); // null
-
使用循环来 replace
代码:
var str = "<div>我是{{ name }},今年{{ age }}岁。</div>"; var pattern = /{{\s*([a-zA-Z]+)\s*}}/; let patternResult = null; while ((patternResult = pattern.exec(str))) { str = str.replace(patternResult[0], patternResult[1]); } console.log(str); // <div>我是name,今年age岁。</div>
-
替换成真实内容
代码:
const data = { name: "章三", age: 20 }; let str = "<div>我是{{name}},今年{{ age }}岁了。</div>"; const pattern = /{{\s*([a-zA-Z]+)\s*}}/; let patternResult = null; while ((patternResult = pattern.exec(str))) { str = str.replace(patternResult[0], data[patternResult[1]]); } console.log(str); // <div>我是章三,今年20岁了。</div>
2、实现简易的模板引擎
-
定义模板结构
代码:
<script type="text/html" id="tpl-user"> <div>姓名:{{name}}</div> <div>年龄:{{ age }}</div> <div>性别:{{ gender}}</div> <div>住址:{{address }}</div> </script>
-
预调用模板引擎
代码:
<script> // 定义数据 var data = { name: "章三", age: 28, gender: "男", address: "杭州江干4号大街" }; // 调用模板引擎 var htmlStr = template("tpl-user", data); // 渲染HTML结构 document.getElementById("user-box").innerHTML = htmlStr; </script>
-
封装 template 函数
代码:
function template(id, data) { var str = document.getElementById(id).innerHTML; var pattern = /{{\s*([a-zA-Z]+)\s*}}/; var pattResult = null; while ((pattResult = pattern.exec(str))) { str = str.replace(pattResult[0], data[pattResult[1]]); } return str; }
-
导入并使用自定义的模板引擎