Ajax-day02
表单相关操作
表单应用场景
Web2.0典型特征:网站需要有交互(互联网中看到的内容由上网者产生)
通过表单,用户可以向服务器提交数据信息,比如注册账号、登录账号、发微博…..
用户与网站的交互方式:基于表单提交数据给服务器
表单基本结构
- form标签本身具有提交行为,默认提交方式是get
- 表单输入域
- 提交按钮
<!-- 传统的表单提交后服务器返回的结果会覆盖当前页面(整页刷新) -->
<!-- action表示提交表单数据目标url地址 -->
<!-- method表示提交方式 -->
<form action="http://www.liulongbin.top:3006/api/addbook" method="post">
<div>
图书名称:
<input type="text" name="bookname">
</div>
<div>
图书作者:
<input type="text" name="author">
</div>
<div>
图书出版社:
<input type="text" name="publisher">
</div>
<div>
<!-- submit类型默认有提交表单行为 -->
<!-- <input type="submit" value="提交"> -->
<!-- button类型没有提交表单行为 -->
<!-- <input type="button" value="提交"> -->
<!-- 如下的按钮也默认有提交行为 -->
<button>提交</button>
</div>
</form>
提交表单:输入域的name属性必须提供并且要与后台要求的名称匹配
form标签的核心属性
- action 表示提交的后台url地址
method 表示提交的方式
- get 默认提交方式
- post
总结:
- 表单输入域的name属性值由后端接口决定
- 按钮的默认提交行为
Form标签属性
- action 提交到后端的地址,如果不写,默认表示当前页面
- method 请求方式,支持get和post,默认是get方式
target 打开action地址的方式(服务器返回的结果在哪里显示)
- _blank 表示打开一个新的页面
- _self 表示在当前页面刷新(默认值)
enctype 发送数据的编码方式(设置前端提交给服务器的数据格式)
默认值 application/x-www-form-urlencoded
- bookname=abc&author=lisi&publisher=hello
- 用于文件上传 multipart/form-data
总结
enctype的值类型
- application/x-www-form-urlencoded (默认值,用于普通表单提交)
- multipart/form-data (用于文件上传)
form常用类型标签
- 普通文本框 text
- 文件上传按钮 file
- 密码框 password
- 下拉选框 select
- 多行文本框 textarea
- 单选框 radio
- 多选框 checkbox
- ……
传统表单提交的问题与解决方法
- 表单页面在提交后会发生跳转,如何不跳转页面?
- 解决办法:使用Ajax方式提交表单
- 第一种方式:Ajax提交表单
<body>
<!-- 传统的表单提交后服务器返回的结果会覆盖当前页面(整页刷新) -->
<!-- action表示提交表单数据目标url地址 -->
<!-- method表示提交方式 -->
<form>
<div>
图书名称:
<input type="text" name="bookname">
</div>
<div>
图书作者:
<input type="text" name="author">
</div>
<div>
图书出版社:
<input type="text" name="publisher">
</div>
<div>
<!-- <button id="btn">提交</button> -->
<input id="btn" type="button" value="提交">
</div>
</form>
<script src="lib/jquery.js"></script>
<script>
// 基于Ajax提交表单
$('#btn').click(function () {
$.post('http://www.liulongbin.top:3006/api/addbook', {
bookname: $('input[name=bookname]').val(),
author: $('input[name=author]').val(),
publisher: $('input[name=publisher]').val()
}, function (ret) {
console.log(ret)
})
})
</script>
</body>
- 第二种方式:基于Ajax提交表单
<form id="myform">
// 基于Ajax提交表单
$('#myform').submit(function (e) {
// 需要手动阻止表单的默认行为
e.preventDefault()
$.post('http://www.liulongbin.top:3006/api/addbook', {
bookname: $('input[name=bookname]').val(),
author: $('input[name=author]').val(),
publisher: $('input[name=publisher]').val()
}, function (ret) {
console.log(ret)
})
})
总结:基于Ajax提交表单的方式
- 使用click事件,该事件需要绑定到按钮上,此时推荐使用 type=’button’的按钮
- 使用submit事件,该事件需要绑定到form标签上,此时推荐使用button标签或者type是submit的按钮,此时需要阻止表单的默认提交行为。
关于表单数据的处理
关于表单数据的获取方式
// jQuery针对表单单独提供了一个方法serialize,用于获取表单的所有的数据
// 表单输入域必须有name属性,并且name的值需要按照接口文档的要求来设置
const formData = $(this).serialize()
注意:必须使用form标签的jQuery实例对象调用serialize方法
发表评论案例
发表评论案例图示如下
- 新闻或者博客文章底部一般都有评论功能
- 提供表单发表评论,评论列表展示
案例基本布局
- 顶部表单区
- 底部列表区
<!-- 评论面板 -->
<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" autocomplete="off" />
<div>评论内容:</div>
<textarea class="form-control" name="content"></textarea>
<button type="submit" class="btn btn-primary">发表评论</button>
</form>
</div>
<!-- 评论列表 -->
<ul class="list-group" id="cmt-list">
<li class="list-group-item">
<span class="badge" style="background-color: #F0AD4E;">评论时间:</span>
<span class="badge" style="background-color: #5BC0DE;">评论人:</span>
Item 1
</li>
</ul>
获取评论列表数据
- 页面加载时调用接口获取评论列表数据
// 加载评论列表数据
function loadCommentList () {
$.get('http://www.liulongbin.top:3006/api/cmtlist', function (res) {
if (res.status === 200) {
// 获取数据成功
const list = res.data
} else {
alert(res.message)
}
})
}
loadCommentList()
总结:页面打开时,直接调用接口获取评论列表数据。
渲染评论列表
- 解析数据拼接字符串(ES6的模板字符串拼接数据)
// 获取评论列表数据
if (ret.status === 200) {
const list = ret.data
let rowTag = ''
$.each(list, function (index, item) {
rowTag += `
<li class="list-group-item">
<span class="badge" style="background-color: #F0AD4E;">评论时间:${item.time}</span>
<span class="badge" style="background-color: #5BC0DE;">评论人:${item.username}</span>
${item.content}
</li>
`
})
$('#cmt-list').html(rowTag)
} else {
alert(ret.msg)
}
总结:把数据填充到页面的过程称之为渲染,前端渲染。
实现发表评论功能
绑定事件
- form表单元素绑定submit事件
- 按钮元素绑定click事件
获取表单数据提交到后台
$(this).serialize()
- 重置表单方法reset
// 功能2:发表评论
// 1、绑定按钮的点击事件
$('#formAddCmt').submit(function (e) {
e.preventDefault()
// 2、获取表单数据
const formData = $(this).serialize()
// 3、提交表单
$.post('http://www.liulongbin.top:3006/api/addcmt', formData, function (ret) {
// 4、处理返回结果
if (ret.status === 201) {
// 发表成功,刷新列表
loadCommnetList()
// 清空表单:原生DOMAPI中有一个方法reset可以重置表单
$('#formAddCmt').get(0).reset()
} else {
alert(ret.msg)
}
})
})
原生Ajax
Ajax Asynchronous Javascript and Xml 异步的JavaScript和Xml
原生Ajax基本用法
浏览器已经实现了Ajax发送请求的技术XMLHttpRequest(2005之后)
基本使用步骤get post 模板
- 创建xhr对象
- 调用xhr.open()方法准备参数
- 调用xhr.send()方法执行发送动作
- 监听xhr.onreadystatechange事件,用于获取服务器返回数据
// 1、创建xhr实例对象
var xhr = new XMLHttpRequest()
// 2、准备发送请求的相关参数
xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks')
// 3、执行发送请求的动作
xhr.send(null)
// 4、指定回调函数,用于处理服务器的返回
xhr.onreadystatechange = function () {
// 该函数是服务器返回数据后触发,该方法不仅仅触发一次
if (xhr.readyState === 4 && xhr.status === 200) {
// 如果上述两个条件都满足,就可以获取服务器返回的正常数据
// ret 是普通字符串
var ret = xhr.responseText
console.log(ret)
console.log(typeof ret)
}
}
总结:使用原生Ajax发送请求很繁琐。
xhr对象详解-请求参数
get请求方式传参
- url地址查询字符串方式传递参数
xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks?id=1&author=吴承恩')
var param = '?id=1&author=吴承恩'
// params = encodeURI(params)
xhr.open('get', 'http://www.liulongbin.top:3006/api/getbooks' + param)
url编码(url如果传递参数本身是不支持中文,所以get传参时需要编码,如果不进行编码,那么后端可能得不到正确的数据)
- encodeURI() 编码函数
- decodeURI() 解码函数
var str = '黑马程序员'
var str1 = encodeURI(str)
console.log(str1)
var str2 = decodeURI('%E9%BB%91%E9%A9%AC')
console.log(str2)
post请求方式传参
- 必须设置请求头
- 必须在send方法调用前设置请求头
// 设置 Content-Type 请求头(告诉服务器前端传递过去的数据格式)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 调用 send 函数
xhr.send('bookname=水浒传&author=施耐庵&publisher=上海图书出版社')
xhr对象详解-响应状态
readyState
- 如果该状态值为4表示服务器已经完全把数据返回了,此时可以获取后台的数据
- 仅仅表示数据已经得到,但是正确与否不确定
status
- http协议的状态:如果status是200表示数据正常,否则表示数据不正常
// readyState状态值是4表示服务器数据已经完全返回
// status的值是200表示服务器返回的数据是正常的
// if (xhr.readyState === 4 && xhr.status === 200) {
// // 如果这两个条件都成立,就可以获取服务器数据了
// var res = xhr.responseText
// console.log(typeof res)
// } else {
// alert(xhr.responseText)
// }
// ---------------------------------------------------
// 考虑异常情况,需要用这种写法
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 成功获取服务器数据
var res = xhr.responseText
} else {
alert('服务器发生错误')
}
}
总结
- xhr.readyState===4表示后端已经把数据完整返回,但是正常与否不确定
- xhr.status===200表示服务器返回的数据是正常的,否则是异常的
xhr对象详解-响应数据
数据格式分析
xml (类似于html格式,标签包裹数据)
- 缺点1:冗余数据比较多,不方便网络传输
- 缺点2:不方便前端解析
json(本质上是字符串,但是有规则)
- 解决XML的两个问题
JSON基本规则(JSON是字符串的一种格式)
- 数据结构为键值对 uname: ‘lisi’
- key 必须是使用英文的双引号包裹的字符串 “uname”:”lisi”
- 字符串类型的值必须使用双引号包裹 “uname”:”lisi”
- JSON 中不能写注释
- JSON 的最外层必须是对象或数组格式
- 不能使用 undefined 或函数作为 JSON 的值
- value 的数据类型可以是数字、字符串、布尔值、null、数组、对象6种类型
JSON格式转换(JSON和对象之间的关系)
- 对象转字符串 JSON.stringify()
- 字符串转对象 JSON.parse()
var jsonStr = '{"a": "Hello", "b": "world"}'
// 把JSON字符串转成对象
var obj = JSON.parse(jsonStr)
console.log(obj)
// 把对象转成JSON字符串
var obj2 = { a: 'hello', b: 'world', c: false }
var str = JSON.stringify(obj2)
console.log(str)
Ajax返回的结果处理
- xhr.responseText本身是字符串
var obj = JSON.parse(xhr.responseText)
总结:
- JSON格式的具体规范
- JSON字符串与对象之间的转换
模仿$.ajax封装函数
- 基本结构分析:回调函数参数如何得到
- 参数处理:支持get和post
- 响应数据处理:转换为对象
function myAjax (options) {
// console.log(options.success)
// 这里的函数调用调用的是谁?其实就是回调函数success
// 通过原生Ajax发送请求并且获取服务器返回的数据
// 请求参数参数处理
/*
{
id: 1,
author: 'tom'
}
把上述数据转换为如下的格式
id=1&author=tom
*/
var params = ''
for (var key in options.data) {
// 每次遍历拼接一个属性
params += key + '=' + options.data[key] + '&'
}
// id=1&author=tom&
if (params) {
// 去掉最后一个字符
params = params.substring(0, params.length - 1)
}
var xhr = new XMLHttpRequest()
// 处理get请求参数
if (options.type === 'get') {
options.url = options.url + '?' + encodeURI(params)
}
xhr.open(options.type, options.url)
if (options.type === 'post') {
// 处理post请求传参
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(params)
} else {
// 执行get请求动作
xhr.send(null)
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// 服务端返回的原始数据
var res = xhr.responseText
res = JSON.parse(res)
options.success(res)
}
}
}
}
总结
表单相关操作
- 表单的作用:与后端交互
- 表单的基本结构
- 表单本身就有提交数据功能,会自动刷新网页
可以通过Ajax方式提交表单,可以不刷新网页
- 基于click事件提交
- 基于submit事件提交
发表评论案例
- 动态渲染评论列表
- 点击发表评论
- 获取所有表单数据serialize()
- 重置表单 reset()
原生Ajax基本使用
基本使用步骤
- 创建xhr实例对象
- 准备请求参数 xhr.open
- 执行发送动作 xhr.send
- 监听服务器返回 xhr.onreadystatechange = function(){}
细节分析
- get请求传参(通过url地址传递),需要处理中文的编码
- post请求传参(通过send传递),需要设置请求头
关于响应状态
- readyState===4表示服务器已经完全返回了数据
- status===200表示返回的数据是正常的
关于响应的结果
- 了解XML数据格式
- 熟悉JSON格式的具体规则
- JSON字符串和对象之间的转换
原生Ajax升级版
XHR2.0介绍
旧版的缺点
- 仅仅支持文本数据传输,无法上传文件(早期文件上传基于Flash)
- 传送数据没有进度提示
新版功能
- 可以使用FormData传递表单数据
- 可以上传文件
- 传输数据时进行提示
FormData基本用法
- FormData是标准的js构造函数(WebAPI)
- FormData作用是啥?传递post请求参数(一般数据来源于表单)
- 如何知道FormData实例对象有几个方法?看官网
FormData怎么用来传参?两种用法
- 构造函数不传参使用append方法添加参数
- 构造函数传递form元素作为参数
用法一:
- 造函数不传参使用append方法添加参数
- 不需要设置请求头
var xhr = new XMLHttpRequest()
xhr.open('post', 'http://www.liulongbin.top:3006/api/formdata')
var fd = new FormData()
fd.append('bookname', '水浒传')
fd.append('author', '施耐庵')
fd.append('publisher', '出版社')
// xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(fd)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var res = xhr.responseText
console.log(res)
}
}
用法二:
- 这种方式,表单的输入域必须提供name属性
// 通过 DOM 操作,获取到 form 表单元素
var form = document.querySelector('#form1')
// 创建 FormData,快速获取到 form 表单中的数据
var fd = new FormData(form)
// 发送请求
xhr.send(fd)
FormData相关方法
- fd.append
- fd.get
- fd.has
- fd.delete
var fd = new FormData()
fd.append('msg', 'nihao')
// var v = fd.get('msg')
// fd.delete('msg')
var f = fd.has('msg')
console.log(f)
FormData文件上传
文件上传的本质:把本地的文件传递到服务器并进行存储,然后返回文件的URL
- UI布局需要用到file标签
- 判断是否选择文件
- 向FormData中追加文件
- 使用xhr发起上传文件请求
- 监听onreadystatechange事件
<!-- 1. 文件选择框 -->
<input type="file" id="file1" />
<!-- 2. 上传文件的按钮 -->
<button id="btnUpload">上传文件</button>
// 点击提交按钮进行文件上传
var btn = document.getElementById('btn')
btn.onclick = function (e) {
e.preventDefault()
var xhr = new XMLHttpRequest()
xhr.open('post', 'http://www.liulongbin.top:3006/api/upload/avatar')
// 如何得到选中的文件?
// file标签DOM元素的files属性表示选中的所有文件
// files不是数组,而是伪数组
var files = document.getElementById('myfile').files
console.log(files)
// 把文件添加到FormData中(avatar由后端决定)
var fd = new FormData()
fd.append('avatar', files[0])
xhr.send(fd)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var res = xhr.responseText
// 得到上传成功后图片的路径信息
res = JSON.parse(res)
var img = document.getElementById('myimg')
// 把图片显示出来
img.setAttribute('src', 'http://www.liulongbin.top:3006' + res.url)
}
}
}
- 直接基于Form表单也可以进行文件上传
<form>
<input type="file" name="avatar" id="myfile">
<button id="btn">点击</button>
</form>
// 通过FormData上传文件
var form = document.querySelector('form')
var fd = new FormData(form)
上传文件进度提示
上传大一点儿的文件需要一些时间,这段时间最好给一个提示,提升用户体验
xhr.upload.onprogress = function (e) {}
- 监听文件上传进度
xhr.upload.onload = function () {}
- 监听文件上传完成事件
<progress id="pg" max="100" value="0"></progress>
<!-- bootstrap 中的进度条 -->
<div class="progress" style="width: 500px; margin: 15px 10px;">
<div class="progress-bar progress-bar-striped active" style="width: 0%" id="percent">
0%
</div>
</div>
// 获取到用户选择的文件列表
var files = document.querySelector('#file1').files
if (files.length <= 0) {
return alert('请选择要上传的文件!')
}
var fd = new FormData()
// 将用户选择的文件,添加到 FormData 中
fd.append('avatar', files[0])
var xhr = new XMLHttpRequest()
// 监听文件上传的进度
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
// 判断文件是否正在上传
// 已经上传的大小/文件的总大小 = 进度
var rate = Math.ceil((e.loaded / e.total) * 100)
// 把进度数据应用到进度条上
var bar = document.getElementById('percent')
// 修改进度条的进度
bar.style = 'width: '+ rate +'%'
// 进度条显示的文字进度
bar.innerHTML = rate + '%'
}
}
// 监听文件上传完成的动作
xhr.upload.onload = function () {
// 文件上传完成后触发,修改进度条颜色
var bar = document.getElementById('percent')
// DOM元素的classList可以操作类名
// 删除已有的类名
bar.classList.remove('progress-bar-striped')
// 添加一个新的类名
bar.classList.add('progress-bar-success')
}
xhr.open('POST', 'http://www.liulongbin.top:3006/api/upload/avatar')
xhr.send(fd)
axios
axios介绍
axios 是一个专门用于调用后台接口的js库,后续Vue和React环境都可以使用它
- 支持客户端发送Ajax请求
- 支持服务端Node.js发送请求
- 支持Promise相关用法(解决回调地狱问题)
- 支持请求和响应的拦截器功能(登录时会用到)
- 可以手动取消请求
- 自动转换JSON数据(JSON.parse(res))
- 可以更加安全的处理请求
基本用法
- 发送请求通过axios.get
获取结果通过then方法
- 对于返回的结果来说,axios会在后台返回的数据外面包裹一层data属性
- 假如后台返回的数据是 {status: 200, msg: ‘返回成功’,data: [{}]}
- 那么axios实际得到的数据是 {data: {status: 200, msg: ‘返回成功’,data: [{}]}}
axios.get('http://www.liulongbin.top:3006/api/getbooks')
.then(function (res) {
// 这里用于获取服务端返回的结果
// res.data中的data是由axios规定的名称,表示从后台得到的数据
console.log(res.data)
})
axios的get用法
get 请求数据并传递参数
get方法第二个参数是配置对象 axios(url, [, config])
- params 用于传递get参数,它的值为对象,对象中用于传递键值对形式参数
- 发送请求时,params中的参数会自动拼接到url中进行发送
- params 是专门用于get请求
// 上述传参方式不太方便
axios.get('http://www.liulongbin.top:3006/api/getbooks', {
// params名称是否固定?是的,是axios的规定
params: {
id: 1,
bookname: 'nihao'
}
}).then(function (res) {
// 这里用于获取服务端返回的结果
console.log(res)
})
// api/get 接口专门测试get请求,请求参数是什么,就返回什么
axios.get('http://www.liulongbin.top:3006/api/get', {
// params名称是否固定?是的,是axios的规定
params: {
id: 1,
bookname: 'nihao',
publisher: 'abc'
}
}).then(function (res) {
// 这里用于获取服务端返回的结果
console.log(res)
})
axios发送post请求
post请求数据传递
传统的表单提交的数据格式 content-type: application/x-www.form-urlencoded
- 后端一般默认支持这种提交的参数格式
post请求参数如果是对象,默认发送的是json形式的参数(content-type: application/json)
- 这种方式的请求参数也需要后台提供支持
// axios发送post请求 post(url[, data[, config]])
// post 参数一:请求地址
// post 参数二:表示请求参数,可选的
// post 参数三:表示配置对象,可选的
// 传统的表单提交的数据格式 content-type: application/x-www.form-urnencoded
// 如下的请求方式,默认发送的是json形式的参数(content-type: application/json)
// 这种方式的请求参数也需要后台提供支持
axios.post('http://www.liulongbin.top:3006/api/post', {
uname: 'lisi',
pwd: '123'
})
.then (function (res) {
console.log(res)
})
- 强制采用content-type: application/x-www.form-urnencoded 请求参数格式
// axios发送post请求 post(url[, data[, config]])
// post 参数一:请求地址
// post 参数二:表示请求参数,可选的
// post 参数三:表示配置对象,可选的
// 强制post发送这种格式请求参数 content-type: application/x-www.form-urlencoded
// 类似于FormData的用法,专门用于参数传递
// URLSearchParams 是一个标准的构造函数,类似于FormData,属于新的API
var fd = new URLSearchParams()
fd.append('uname', 'lisi')
fd.append('pwd', '123')
// 发送请求时会转换为如下格式
// uname=lisi&pwd=123
// 这种格式的请求参数,服务器一般默认都支持
axios.post('http://www.liulongbin.top:3006/api/post', fd)
.then (function (res) {
console.log(res)
})
另一种用法
axios作为方法使用
- 支持各种请求方式
- 支持各种方式传参
// axios更加通用的调用接口方式
// axios({
// // 表示请求方式
// method: 'get',
// url: 'http://www.liulongbin.top:3006/api/get',
// params: {
// id: 1,
// uname: 'lisi'
// }
// }).then(function (res) {
// console.log(res)
// })
// 发送请求参数 json格式
// axios({
// // 表示请求方式
// method: 'post',
// url: 'http://www.liulongbin.top:3006/api/post',
// data: {
// id: 123,
// uname: 'lisi',
// age: 12
// }
// }).then(function (res) {
// console.log(res)
// })
// 发送请求参数 form格式
// var fd = new URLSearchParams()
// fd.append('uname', 'lisi')
// fd.append('pwd', '123')
// axios({
// // 表示请求方式
// method: 'post',
// url: 'http://www.liulongbin.top:3006/api/post',
// data: fd
// }).then(function (res) {
// console.log(res)
// })
全局默认配置
配置基准URL地址
- 做项目时,基准一般配置一次即可,发送请求时检修url后面的路径即可
axios.defaults.baseURL = 'http://www.liulongbin.top:3006/'
axios.get('api/get?id=1').then(function (res) {
console.log(res)
})