如何在 Vue.js 中构建实时可编辑的数据表
项目设置
添加数据表组件
添加 Hamoni 同步
拍摄结束!
资源
在数据驱动型应用中,数据表用于以表格形式展示数据,并允许用户直接编辑和删除记录。使用Vue框架,您可以利用各种开源组件轻松地将数据表添加到应用中。如今,许多应用都具备实时功能,您可能会问:如何实现数据的编辑和删除实时同步?您可以采用以下三种方法:
-
使用WebSocket API。但如果部分用户使用的浏览器尚不支持 WebSocket,则这不是一个好的选择。
-
使用能够抽象化这些跨浏览器差异并提供回退机制的库,例如Socket.IO、SignalR和SockJS。但选择这种方式,您需要管理处理大量打开连接的服务器,并解决扩展性问题。
-
使用提供库的服务,该库的功能与之前的选项相同,但可以管理服务器并进行适当的扩展。对于正在采用(或已经采用)无服务器架构的公司和团队来说,这是一个更可取的选择。
我将向您展示如何使用Hamoni Sync作为实时状态同步服务,在 Vue.js 中构建一个可实时编辑的数据表。下图展示了我们将要构建的内容:
要跟着教程操作,你需要具备一些 Vue 的基础知识。如果你完全不了解 Vue,可以阅读我之前的文章快速入门 Vue.js。你还需要以下工具:
-
Node.js 和 npm(点击链接下载适用于您操作系统的安装程序)
-
Vue CLI用于搭建新的 Vue 项目。如果没有安装 Vue CLI,请
npm install -g vue-cli@2.9.6从命令行运行命令进行安装。
项目设置
我们将使用 Vue CLI 和Vuetify的模板来搭建项目。打开命令行并运行命令。系统会提示您输入项目名称和作者,请按回车键接受默认值。这将创建一个包含单个文件的新 Vue 项目。该文件包含对 Vue 和 Vuetify 的脚本引用。Vuetify 是一个适用于 Vue.js 的Material Design组件。它包含一个具有排序、搜索、分页、行内编辑、标题工具提示和行选择等功能的组件。vue init vuetifyjs/simple realtime-datatable-vueindex.htmlv-data-table
添加数据表组件
index.html使用文本编辑器(或集成开发环境)打开文件。将第50行的内容替换为以下内容:
<div>
<v-dialog v-model="dialog" max-width="500px">
<v-btn slot="activator" color="primary" dark class="mb-2">New Item</v-btn>
<v-card>
<v-card-title>
<span class="headline">{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container grid-list-md>
<v-layout wrap>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field v-model="editedItem.protein" label="Protein (g)"></v-text-field>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" flat @click.native="close">Cancel</v-btn>
<v-btn color="blue darken-1" flat @click.native="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-data-table :headers="headers" :items="desserts" hide-actions class="elevation-1">
<template slot="items" slot-scope="props">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.calories }}</td>
<td class="text-xs-right">{{ props.item.fat }}</td>
<td class="text-xs-right">{{ props.item.carbs }}</td>
<td class="text-xs-right">{{ props.item.protein }}</td>
<td class="justify-center layout px-0">
<v-btn icon class="mx-0" @click="editItem(props.item)">
<v-icon color="teal">edit</v-icon>
</v-btn>
<v-btn icon class="mx-0" @click="deleteItem(props.item)">
<v-icon color="pink">delete</v-icon>
</v-btn>
</td>
</template>
</v-data-table>
</div>
上面的代码添加了一个v-dialog组件,用于显示对话框以收集新记录的数据或编辑现有记录。此外,它还添加了v-data-table一个用于渲染表格的组件。我们需要定义这些组件使用的数据和方法。在第126行之后,将以下代码添加到数据属性中:
dialog: false,
headers: [
{
text: 'Dessert (100g serving)',
align: 'left',
sortable: false,
value: 'name'
},
{ text: 'Calories', value: 'calories' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Actions', value: 'name', sortable: false }
],
desserts: [],
editedIndex: -1,
editedItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0
},
defaultItem: {
name: '',
calories: 0,
fat: 0,
carbs: 0,
protein: 0
},
listPrimitive: null
datadesserts属性将保存要在表格中显示的数据。该editedItem属性将保存正在编辑的记录的值,而该属性editedIndex将保存正在编辑的记录的索引。
data在属性定义之后,第189行之后,添加以下属性:
computed: {
formTitle() {
return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
}
},
watch: {
dialog(val) {
val || this.close()
}
},
我们添加了一个 ` computedtitle`watch属性。`title`computed属性定义formTitle了对话框组件的标题,该标题基于 `title` 的值editedIndex。`title`watch属性会监听`title` 的值何时发生变化。如果 `title` 的值变为 `false`,则会调用稍后定义的dialog函数。close()
添加 Hamoni 同步
此时我们需要添加 Hamoni Sync。它用于同步应用程序状态,并处理冲突,以避免一个用户的数据覆盖另一个用户的数据。要使用 Hamoni Sync,您需要注册一个帐户并创建应用程序 ID。请按照以下步骤在 Hamoni 中创建应用程序。
- 注册并登录 Hamoni控制面板。
- 在文本框中输入您想要的应用名称,然后点击“创建”按钮。这样就会创建应用并将其显示在应用列表中。
- 展开账户ID卡即可查看您的账户ID
在第139行 Vuetify 脚本引用下方,添加对 Hamoni Sync 的引用。
<script src="https://unpkg.com/hamoni-sync@1.1.1/hamoni.dev.js"></script>
然后,我们需要在 Vue 组件挂载后初始化 Hamoni Sync。在属性mounted下方添加一个属性。watch
mounted: function () {
const accountId = "YOUR_ACCOUNT_ID";
const appId = "YOUR_APP_ID";
let hamoni;
fetch("https://api.sync.hamoni.tech/v1/token", {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8"
},
body: JSON.stringify({ accountId, appId })
}).then(response => {
response.json().then(token => {
hamoni = new Hamoni(token);
hamoni.connect().then(() => {
hamoni.get("vue-table").then(primitive => {
this.listPrimitive = primitive
this.desserts = [...primitive.getAll()]
this.subscribeToUpdate()
}).catch(error => {
if (error === "Error getting state from server")
this.initialise(hamoni);
else
alert(error)
})
}).catch(alert)
})
})
},
上面的代码使用来自 Sync 令牌 API 的令牌初始化 Hamoni Sync。您需要帐户 ID 和应用程序 ID 才能从 API 获取身份验证令牌。请将字符串占位符替换为控制面板中的帐户 ID 和应用程序 ID。建议从后端调用 Sync 令牌服务器并将响应令牌发送给客户端应用程序。在本示例中,我将它们全部放在一起。
hamoni.connect()然后,它通过调用返回 Promise 的函数连接到 Hamoni 服务器。连接成功后,我们传入hamoni.get()存储在 Hamoni 中的状态名称进行调用。要从 Hamoni 获取状态,该状态必须已经创建,否则会返回错误。我在这里所做的就是在 catch 代码块中处理此错误,以便调用另一个函数来初始化 Hamoni Sync 中的状态。如果获取应用程序状态的调用成功,它将返回一个对象,该对象将用于修改该状态中包含的数据。此对象称为 Sync 原语。Sync 原语有 3 种类型:
-
值基元:这种状态保存的是简单的信息,可以用字符串、布尔值或数字等数据类型表示。它最适合用于未读邮件计数、开关状态等情况。
-
对象原语:对象状态表示可以建模为 JavaScript 对象的状态。例如,它可以用于存储游戏的分数。
-
List 基本类型:它用于存储状态对象列表。状态对象是 JavaScript 对象。您可以根据列表项的索引来更新它。
在这个例子中,我们使用了列表基本类型。我们调用primitive.getAll()`getState` 方法并将其传递给 `getState`desserts函数。之后,它会调用 `getState` 函数subscribeToUpdate()。该函数将用于订阅来自 Hamoni Sync 的状态更改事件。
在第215mounted行的属性之后添加以下代码:
methods: {
initialise(hamoni) {
hamoni.createList("vue-table", [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0
},
{
name: 'Ice cream sandwich',
calories: 237,
fat: 9.0,
carbs: 37,
protein: 4.3
},
{
name: 'Eclair',
calories: 262,
fat: 16.0,
carbs: 23,
protein: 6.0
}
]).then(primitive => {
this.listPrimitive = primitive
this.desserts = this.listPrimitive.getAll()
this.subscribeToUpdate();
}).catch(alert)
},
subscribeToUpdate() {
this.listPrimitive.onItemAdded(item => {
this.desserts.push(item.value)
})
this.listPrimitive.onItemUpdated(item => {
//update the item at item.index
this.desserts.splice(item.index, 1, item.value);
})
this.listPrimitive.onItemRemoved(item => {
//remove the item at item.index
this.desserts.splice(item.index, 1);
})
},
editItem(item) {
this.editedIndex = this.desserts.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem(item) {
const index = this.desserts.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.listPrimitive.remove(index)
},
close() {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save() {
if (this.editedIndex > -1) {
this.listPrimitive.update(this.editedIndex, this.editedItem)
} else {
this.listPrimitive.add(this.editedItem)
}
this.close()
}
}
上面的代码定义了我们目前为止引用的函数。该initialise()函数创建一个名为 `<list_name>` 的列表vue-table。这些subscribeToUpdate()函数包含用于处理列表项添加、更新或删除操作的代码。该函数通过调用 `remove` 函数并传入要删除项的索引deleteItem()来从列表中删除项。该函数调用 ` add` 函数向列表添加新项,并调用 `update` 函数更新指定索引处的记录。listPrimitive.remove(index)save()listPrimitive.add(editedItem)listPrimitive.update(editedIndex, editedItem)
这就是实现实时可编辑数据表所需的所有代码。index.html在浏览器中打开文件,应用程序即可使用!
拍摄结束!
我们用 Vue.js 构建了一个可实时编辑的数据表。Hamoni Sync可以轻松添加实时功能。如果您使用构建系统和单文件组件,Vuetify和Hamoni Sync都提供了 npm 包。您可以在GitHub上找到源代码。
