组件化的可编辑数据表格
设计思路
可编辑表格中的数据来自于服务端,由 JSON 格式表示。实质上就是在编辑表格数据时,异步更新 json 文件中的数据。
实现方法
服务端数据
通过 json-server 来模拟后台数据接口
1.先项目目录中创建一个 data.json 文件,存放表格中显示的数据
{
"student": [
{
"id": 1008,
"name": "李明",
"yw": "100",
"sx": 55,
"yy": 66
},
{
"id": 1009,
"name": "阿梅",
"yw": 50,
"sx": "50",
"yy": 99
},
{
"id": 1010,
"name": "阿四",
"yw": "90",
"sx": 80,
"yy": 80
}
]
}
2.在 data.json 文件目录下启动服务 json-server --watch data.json
如何实现异步更新数据呢?我们想到的解决方法就是通过 ajax 请求来实现异步操作。
核心 JS 代码设计
除了基本的 html(表格页面)和 css(表格样式)
主要的 js 分为三个类 table,student,tips
table 类:用来渲染表格。
通过 async 函数封装 ajax 中的 GET 请求获取 json 文件中的学生数据,
async getUserData() {
var url = "http://localhost:3000/student";
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
if (xhr.status === 200) {
var studentsData = JSON.parse(xhr.responseText);
var student;
new Tips("加载成功!!", Tips.TipsClassName.success).showTips();
for (student of studentsData) {
this.students.push(new Student(student));
}
} else {
new Tips("加载失败!!", Tips.TipsClassName.error).showTips();
console.error('Error: ' + xhr.status);
}
}
加载表格中的数据
//加载数据
async loadUserTable() {
var that = this;
var userData = this.students;
var tbody = document.getElementById("tbody");
var innerHtml = "";
var student;
for (student of userData) {
innerHtml += `
<tr>
<td data-id="${student.id}">${student.id}</td>
<td name="name" data-id="${student.id}" >${student.name}</td>
<td name="grade" data-id="${student.id}" data-name="yw">${student.yw}</td>
<td name="grade" data-id="${student.id}" data-name="sx">${student.sx}</td>
<td name="grade" data-id="${student.id}" data-name="yy">${student.yy}</td>
<td rname="allgrade" data-id="${student.id}">${student.getAllNum()}</td>
<td rname="allgrade"><button id="studentDelBtn${student.id}" data-id="${student.id}">删除</button></td>
</tr>`;
}
tbody.innerHTML = innerHtml;
绑定删除的点击事件
//事件绑定
for (student of userData) {
document.getElementById(`studentDelBtn${student.id}`).onclick =
function () {
that.delStudent(this.dataset.id);
};
}
给单元格添加点击事件,触发更新单元格内容的函数。
// 给单元格添加点击事件
async setCellCilck() {
var that = this;
var grades = document.getElementsByName("grade");
var names = document.getElementsByName("name");
for (let i = 0; i < grades.length; i++) {
grades[i].onclick = function () {
that.updateCell(this);
};
}
for (let i = 0; i < names.length; i++) {
names[i].onclick = function () {
that.updateCell(this);
};
}
}
更新单元格内容函数中,给不同的可编辑列提供修改规则
// 更新单元格内容
async updateCell(ele) {
var that = this;
if (document.getElementsByClassName("active-input").length == 0) {
var oldhtml = ele.innerHTML;
ele.innerHTML = "";
// 通过DOM API 创建input元素,设置属性,值,方法
var newInput = document.createElement("input");
newInput.setAttribute("class", "active-input");
newInput.value = oldhtml;
newInput.onblur = function () {
var name = ele.getAttribute("name");
if (name == "name") {
const isValid = /^[\u4e00-\u9fa5]+$/.test(this.value);
if (isValid) {
//修改数据
ele.innerHTML = this.value;
//修改对象
const id = ele.dataset.id;
that.updStudent(id, { name: this.value });
} else {
new Tips("请正确输入您的姓名!", Tips.TipsClassName.error).showTips();
ele.innerHTML = oldhtml;
}
} else if (name == "grade") {
const isValid = /^(\d|[1-9]\d|100)(\.\d+)?$/.test(this.value);
if (isValid) {
//修改数据
ele.innerHTML = this.value;
//修改对象
const id = ele.dataset.id;
var data = {};
data[ele.dataset.name] = this.value;
//按断文案
that.updStudent(id, data);
} else {
new Tips("请输入0-100!", Tips.TipsClassName.error).showTips();
ele.innerHTML = oldhtml;
}
}
};
newInput.select();
ele.appendChild(newInput);
newInput.focus();
} else {
return;
}
}
通过 getAttribute()获取属性,
当属性值为 name 时修改姓名,再通过正则表达式/[1]+$/.test(this.value),来判断输入的值是否为中文,
TRUE 则修改当前数据,并修改对象;
FALSE,则调用 Tips 类,显示错误信息,并返回原来的值。
修改其他数据同理。
更新单元格函数中调用的 更新学生信息函数 以及 删除学生信息函数 都是通过检验 id 是否与 json 数据中学生 id 匹配来调用 student 类中的更新或删除方法。
student 类:json 数据封装,获取请求(PUT,DELETE)
constructor({ id, name, yw, sx, yy }) {
this.id = id;
this.name = name;
this.yw = parseFloat(yw);
this.sx = parseFloat(sx);
this.yy = parseFloat(yy);
}
getAllNum() {
return this.sx + this.yw + this.yy;
}
修改可编辑表格信息:ajax 中的 PUT 请求
async upd(newStudent) {
Object.assign(this, newStudent);
var url = "http://localhost:3000/student/" + this.id;
const xhr = new XMLHttpRequest();
xhr.open('PUT', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.addEventListener("load", () => {
if (xhr.status == 200) {
new Tips("修改成功!!", Tips.TipsClassName.success).showTips();
} else {
new Tips("修改失败!!", Tips.TipsClassName.error).showTips();
}
});
xhr.send(JSON.stringify(this));
}
删除数据行:DELETE 请求
async del() {
var url = "http://localhost:3000/student/" + this.id;
const xhr = new XMLHttpRequest();
xhr.open('DELETE', url);
xhr.addEventListener("load", () => {
if (xhr.status == 200) {
new Tips("删除成功!!", Tips.TipsClassName.success).showTips();
} else {
new Tips("删除失败!!", Tips.TipsClassName.error).showTips();
}
});
xhr.send();
}
tips 类:提示信息框
export default class Tips {
static TipsClassName = {
error: "err movedown",
success: "success movedown",
};
constructor(text = "提示框", classname = Tips.TipsClassName.error) {
this.text = text;
this.classname = classname;
}
//错误提示
showTips() {
var that = this;
var thetips = document.getElementById("tips");
thetips.innerHTML = this.text;
thetips.className = this.classname;
thetips.style.display = "block";
setTimeout(function () {
that.noneTips();
}, 3000);
}
//隐藏错误提示
noneTips() {
var thetips = document.getElementById("tips");
thetips.style.display = "none";
}
}