vue.js 构建项目
by Peter Mbanugo
彼得·姆巴努戈(Peter Mbanugo)
In data-driven applications, a data table is used to display data in a tabular format with the ability to edit and delete records in place. When you’re working with Vue, there are different open-sourced components that can be used to easily add a data table to your application.
在数据驱动的应用程序中,数据表用于以表格格式显示数据,并具有就地编辑和删除记录的功能。 当您使用Vue时 ,可以使用不同的开源组件轻松地将数据表添加到您的应用程序。
Many applications today have real-time features, and you may wonder how you can synchronize editing and deleting data in real-time. There are three options for this:
如今,许多应用程序都具有实时功能,您可能想知道如何实时同步编辑和删除数据。 共有三个选项:
Use WebSocket API. This is not a good option if some of your users are using browsers that don’t yet support WebSocket.
使用WebSocket API 。 如果您的某些用户使用的浏览器尚不支持WebSocket,则这不是一个好选择。
Use a library that abstracts away these cross-browser differences with a fallback mechanism, such as Socket.IO, SignalR, and SockJS. With this option, you’d have to manage the server that handles a large number of open connections and deal with scaling.
使用一个库,该库通过后备机制(例如Socket.IO , SignalR和SockJS)来抽象化这些跨浏览器的差异。 使用此选项,您将不得不管理处理大量打开的连接并进行扩展的服务器。
I’ll show you how to build a real-time editable data table in Vue.js using Hamoni Sync as the real-time state synchronisation service. The picture below shows what we’ll build:
我将向您展示如何使用Hamoni Sync作为实时状态同步服务在Vue.js中构建实时可编辑数据表。 下图显示了我们将要构建的内容:
To follow along, you’ll need to have some basic knowledge of Vue. If you have no knowledge of Vue, you can read my previous post to get up to speed with Vue.js. You’ll also need the following tools:
要继续学习,您需要具备Vue的一些基本知识。 如果您不了解Vue,则可以阅读我以前的文章 ,以快速掌握Vue.js。 您还需要以下工具:
Node.js & npm (follow the link to download an installer for your OS)
Node.js和npm (点击链接以下载适用于您的操作系统的安装程序)
Vue CLI to scaffold a new Vue project. If you don’t have this, run npm install -g vue-cli@2.9.6
from the command line to install it.
Vue CLI支持新的Vue项目。 如果没有,请从命令行运行npm install -g vue-cli@2.9.6
进行安装。
We’ll set up the project using the Vue CLI and a template from Vuetify. Open the command line and run the command vue init vuetifyjs/simple realtime-datatable-vue
. You'll get asked for a name and an author, so accept the default value by hitting enter for each prompt. This will scaffold a new Vue project with a single index.html
file.
我们将使用Vue CLI和来自Vuetify的模板来设置项目。 打开命令行并运行命令vue init vuetifyjs/simple realtime-datatable-vue
。 系统会要求您输入名称和作者,因此请在每个提示中按Enter接受默认值。 这将使用单个index.html
文件搭建一个新的Vue项目。
This file contains script references to Vue and Vuetify. Vuetify is a Material Design Component for Vue.js. It has a v-data-table
component with features for sorting, searching, pagination, inline-editing, header tooltips, and row selection.
该文件包含对Vue和Vuetify的脚本引用。 Vuetify是Vue.js的材料设计组件。 它具有一个v-data-table
组件,具有用于排序,搜索,分页,内联编辑,标题工具提示和行选择的功能。
Open the file index.html
with your text editor (or IDE). Replace the content on line 50 with the following:
使用文本编辑器(或IDE)打开文件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>
The code above adds a v-dialog
component for displaying a dialog to collect data for new records or edit an existing record. Also, it adds the v-data-table
which renders the table. We need to define the data and methods used by these components. After line 126, add the following code to the data properties:
上面的代码添加了一个v-dialog
组件,用于显示对话框以收集新记录的数据或编辑现有记录。 此外,它还添加了呈现该表的v-data-table
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
The desserts
data property will hold the data to be displayed in the table. The editedItem
property will hold values for the record being edited, and the editedIndex
will hold the index of the record being edited.
desserts
数据属性将保存要在表中显示的数据。 editedItem
属性将保存正在编辑的记录的值, editedIndex
将保存正在编辑的记录的索引。
Add the following properties after the data
property definition, after line 189:
在第189行的data
属性定义之后添加以下属性:
computed: { formTitle() { return this.editedIndex === -1 ? 'New Item' : 'Edit Item' }},
watch: { dialog(val) { val || this.close() }},
We added a computed
and watch
property. The computed
property defines formTitle
which gives the dialog component a title based on the value of editedIndex
. The watch
property watches dialog
for when its value changes. If the value changes to false, it calls the function close()
which will be defined later.
我们添加了一个computed
和watch
属性。 computed
formTitle
属性定义formTitle
,该属性根据editedIndex
的值为对话框组件提供标题。 watch
属性watch
其值更改dialog
。 如果该值更改为false,它将调用函数close()
,稍后将对其进行定义。
At this junction we need to add Hamoni Sync. It is used to synchronise the application state, and handles conflict resolution to avoid one user overriding another user’s data. To use Hamoni Sync, you’ll have to sign up for an account and application ID. Follow these steps to create an application in Hamoni.
在这个结点,我们需要添加Hamoni Sync。 它用于同步应用程序状态,并处理冲突解决方案,以避免一个用户覆盖另一个用户的数据。 要使用Hamoni Sync,您必须注册一个帐户和应用程序ID。 请按照以下步骤在Hamoni中创建一个应用程序。
Register and login to the Hamoni dashboard.
注册并登录到Hamoni 仪表板 。
Below the script reference to Vuetify on line 139, add a reference to Hamoni Sync:
在第139行上对Vuetify的脚本引用下面,添加对Hamoni Sync的引用:
<script src="https://unpkg.com/hamoni-sync@0.4.0/hamoni.dev.js"><;/script>
Then we need to initialise Hamoni Sync once the Vue component is mounted. Add a mounted
property below the watch
property:
然后,在安装Vue组件后,我们需要初始化Hamoni Sync。 在watch
属性下面添加一个已mounted
的属性:
mounted: function () { let hamoni = new Hamoni("ACCOUNT_ID", "APP_ID");
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)},
From the code above, we initialize Hamoni Sync with an account and application ID. Replace the string placeholders with the account and application ID from the dashboard. Then it is connected to the Hamoni server by calling hamoni.connect()
which returns a promise.
从上面的代码,我们使用帐户和应用程序ID初始化Hamoni Sync。 用仪表板中的帐户和应用程序ID替换字符串占位符。 然后通过调用hamoni.connect()
将其连接到hamoni.connect()
服务器,该协议返回一个hamoni.connect()
。
Once connected, we call hamoni.get()
with the name of the state stored in Hamoni. In order to retrieve a state from Hamoni, it needs to have been created, otherwise it'll return an error. What I've done here is handle this error within the catch block, such that it calls another function to initialize the state in Hamoni Sync.
连接后,我们使用存储在hamoni.get()
中的状态名称调用hamoni.get()
。 为了从Hamoni检索状态,必须先创建状态,否则将返回错误。 我在这里所做的是在catch块中处理此错误,以便它调用另一个函数来初始化Hamoni Sync中的状态。
If the call to get an application state succeeds, it returns an object which will be used to modify data contained in that state. This object is referred to as a Sync primitive. There are three types of Sync primitives:
如果获取应用程序状态的调用成功,它将返回一个对象,该对象将用于修改该状态中包含的数据。 该对象称为“同步”原语。 同步原语有三种类型:
Value Primitive: This kind of state holds simple information represented with datatypes like string, boolean or numbers. It is best suited for cases such as unread message count, toggles, etc.
值原始值 :这种状态包含简单信息,这些信息用数据类型表示,例如字符串,布尔值或数字。 最适合未读消息计数,切换等情况。
Object Primitive: Object state represents states that can be modelled as a JavaScript object. An example usage could be storing the score of a game.
Object Primitive :对象状态表示可以建模为JavaScript对象的状态。 示例用法可以是存储游戏得分。
List Primitive: This holds a list of state objects. A state object is a JavaScript object. You can update an item based on its index in the list.
列表基元 :包含状态对象的列表。 状态对象是JavaScript对象。 您可以根据列表中的项目索引来更新项目。
We’ve used a list primitive for this example. We call primitive.getAll()
to get the state and pass it to desserts
. After that, it calls the function subscribeToUpdate()
. This function will be used to subscribe to state change events from Hamoni Sync.
在此示例中,我们使用了列表原语。 我们调用primitive.getAll()
来获取状态并将其传递给desserts
。 之后,它将调用函数subscribeToUpdate()
。 此功能将用于从Hamoni Sync订阅状态更改事件。
Add the following code after the mounted
property on line 215:
在第215行的mounted
属性之后添加以下代码:
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.onItemDeleted(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.delete(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.push(this.editedItem) }
this.close() }}
The code above defines the functions we’ve been referencing thus far.
上面的代码定义了到目前为止我们一直在引用的功能。
The initialise()
function creates the list primitive with name as vue-table
.
initialise()
函数创建名称为vue-table
的列表原语。
The subscribeToUpdate()
functions contain code to handle when an item is added, updated, or deleted from the list primitive.
subscribeToUpdate()
函数包含用于在添加,更新或从列表原语中删除项目时处理的代码。
The deleteItem()
function removes an item from the list primitive by calling listPrimitive.delete(index)
with the index of the item to delete.
deleteItem()
函数通过调用带有要删除项目索引的listPrimitive.delete(index)
从列表原语中删除项目。
The save()
function calls listPrimitive.push(editedItem)
to add a new item to the list primitive, and calls listPrimitive.update(editedIndex, editedItem)
to update the record at a certain index.
save()
函数调用listPrimitive.push(editedItem)
将一个新项添加到列表原语,并调用listPrimitive.update(editedIndex, editedItem)
以某个索引更新记录。
This is all the code that’s needed to achieve our objective of a real-time editable data table. Open the index.html
file in your browser and the application is ready to use!
这是实现我们的实时可编辑数据表目标所需的全部代码。 在浏览器中打开index.html
文件,该应用程序就可以使用了!
We’ve built a real-time editable data table in Vue.js. Hamoni Sync makes it easy to add real-time functionality. Both Vuetify and Hamoni Sync have npm packages if you’re working with a build system and using single file components. You can find the source code on GitHub.
我们在Vue.js中建立了一个实时可编辑数据表。 Hamoni Sync使添加实时功能变得容易。 如果您使用的是构建系统并使用单个文件组件,则Vuetify和Hamoni Sync都具有npm软件包。 您可以在GitHub上找到源代码。
vue.js 构建项目