添加新的待辦事項表單:Vue 事件、方法和模型
现在我们已经有了示例数据,以及一个循环,它获取每个数据并将其渲染到应用中的 ToDoItem 中。我们接下来真正需要的是让用户能够将他们自己的待办事项输入到应用程序中,为此我们需要一个文本 <input>,一个当数据提交时触发的事件,一个在提交时触发的添加数据并重新渲染列表的方法,以及一个控制数据的模型。这就是我们将在本文中讨论的内容。
| 先决条件 |
熟悉核心 HTML、CSS 和 JavaScript 语言,了解 终端/命令行。 Vue 组件以 JavaScript 对象(管理应用程序数据)和基于 HTML 的模板语法(映射到底层 DOM 结构)的组合形式编写。要进行安装,并使用 Vue 的一些更高级功能(如单文件组件或渲染函数),您需要一个安装了 node + npm 的终端。 |
|---|---|
| 目标 | 了解如何在 Vue 中处理表单,以及相关的事件、模型和方法。 |
創建新的待辦事項表單
我们现在有一个显示待办事项列表的应用程序。但是,我们无法更新项目列表,除非手动更改代码!让我们解决这个问题。让我们创建一个新的组件,允许我们添加新的待办事项。
- 在您的组件文件夹中,创建一个名为
ToDoForm.vue的新文件。 - 添加一个空白的
<template>和一个<script>标签,就像以前一样。html<template></template> <script> export default {}; </script> - 让我们添加一个 HTML 表单,它允许您输入新的待办事项并将其提交到应用程序中。我们需要一个
<form>,带有一个<label>、一个<input>和一个<button>。更新您的模板,如下所示。因此,我们现在有一个表单组件,可以在其中输入新的待办事项标题(当它最终被渲染时,它将成为相应html<template> <form> <label for="new-todo-input"> What needs to be done? </label> <input type="text" id="new-todo-input" name="new-todo" autocomplete="off" /> <button type="submit">Add</button> </form> </template>ToDoItem的标签)。 - 让我们将此组件加载到我们的应用程序中。返回到
App.vue,并在之前的语句下方添加以下import语句,位于您的<script>元素中。jsimport ToDoForm from "./components/ToDoForm"; - 您还需要在
App组件中注册新的组件 — 更新组件对象的components属性,使其看起来像这样。jscomponents: { ToDoItem, ToDoForm, } - 最后,在本节中,通过将
<to-do-form />元素添加到App的<template>中,将ToDoForm组件渲染到应用程序中,如下所示。html<template> <div id="app"> <h1>My To-Do List</h1> <to-do-form></to-do-form> <ul> <li v-for="item in ToDoItems" :key="item.id"> <to-do-item :label="item.label" :done="item.done" :id="item.id"></to-do-item> </li> </ul> </div> </template>
现在,当您查看运行的网站时,您应该会看到新显示的表单。
如果您填写它并点击“添加”按钮,该页面将把表单发布回服务器,但这并不是我们真正想要的。我们实际上想要做的是在 submit 事件 上运行一个方法,该方法将新的待办事项添加到 App 内定义的 ToDoItem 数据列表中。为此,我们需要向组件实例添加一个方法。
創建方法並使用 v-on 將其綁定到事件
要使一个方法对 ToDoForm 组件可用,我们需要将其添加到组件对象中,这在组件对象的 methods 属性中完成,它位于 data()、props 等的位置。methods 属性保存我们可能需要在组件中调用的任何方法。当引用时,方法会完全运行,因此不要将其用于在模板中显示信息。对于显示来自计算的数据,您应该使用 computed 属性,我们将在稍后介绍。
- 在此组件中,我们需要向
ToDoForm组件对象的methods属性中添加一个onSubmit()方法。我们将使用它来处理提交操作。按如下方式添加它。jsexport default { methods: { onSubmit() { console.log("form submitted"); }, }, }; - 接下来,我们需要将该方法绑定到
<form>元素的submit事件处理程序。与 Vue 如何使用v-bind语法绑定属性非常类似,Vue 还有一个用于事件处理的特殊指令:v-on。v-on指令通过v-on:event="method"语法工作。并且与v-bind类似,也有一种简写语法:@event="method"。为了保持一致性,我们将在这里使用简写语法。将submit处理程序添加到您的<form>元素中,如下所示。html<form @submit="onSubmit">…</form> - 当您运行它时,应用程序仍然将数据发布到服务器,导致刷新。由于我们是在客户端执行所有处理,因此没有服务器来处理回发。我们还会在页面刷新时丢失所有本地状态。要防止浏览器发布到服务器,我们需要在页面冒泡时停止事件的默认操作(
Event.preventDefault(),在普通 JavaScript 中)。Vue 有一种称为 **事件修饰符** 的特殊语法,可以为我们直接在模板中处理这个问题。修饰符以点为后缀附加到事件的末尾,如下所示:@event.modifier。以下是事件修饰符列表。.stop:停止事件传播。等效于普通 JavaScript 事件中的Event.stopPropagation()。.prevent:防止事件的默认行为。等效于Event.preventDefault()。.self:仅当事件从该确切元素发出时才触发处理程序。{.key}:仅通过指定的键触发事件处理程序。MDN 有一个有效的键值列表;多词键只需要转换为 kebab-case(例如page-down)。.native:侦听组件的根(最外层包装)元素上的本机事件。.once:侦听事件,直到它被触发一次,之后不再侦听。.left:仅通过左鼠标按钮事件触发处理程序。.right:仅通过右鼠标按钮事件触发处理程序。.middle:仅通过中间鼠标按钮事件触发处理程序。.passive:等效于在使用addEventListener()的普通 JavaScript 中创建事件侦听器时使用{ passive: true }参数。
.prevent修饰符来停止浏览器的默认提交操作。在您的模板中,将.prevent添加到@submit处理程序中,如下所示。html<form @submit.prevent="onSubmit">…</form>
如果您现在尝试提交表单,您会注意到页面没有重新加载。如果打开控制台,您可以看到我们在 onSubmit() 方法中添加的 console.log() 的结果。
使用 v-model 將數據綁定到輸入
接下来,我们需要一种方法来获取表单的 <input> 的值,以便我们可以将新的待办事项添加到 ToDoItems 数据列表中。
我们首先需要在表单中有一个 data 属性来跟踪待办事项的值。
- 向
ToDoForm组件对象添加一个data()方法,该方法返回一个label字段。我们可以将label的初始值设置为一个空字符串。您的组件对象现在应该看起来像这样。jsexport default { methods: { onSubmit() { console.log("form submitted"); }, }, data() { return { label: "", }; }, }; - 我们现在需要一种方法将
new-todo-input元素的字段的值附加到label字段。Vue 专门为此提供了一个指令:v-model。v-model绑定到您为其设置的数据属性,并使其与<input>保持同步。v-model在所有各种输入类型中都适用,包括复选框、单选按钮和选择输入。要使用v-model,您需要向<input>添加一个具有v-model="variable"结构的属性。因此,在我们的案例中,我们将将其添加到new-todo-input字段中,如下所示。现在就执行此操作。html<input type="text" id="new-todo-input" name="new-todo" autocomplete="off" v-model="label" />注意:您还可以通过事件和
v-bind属性的组合来同步数据与<input>值。事实上,这就是v-model在幕后所做的。但是,确切的事件和属性组合因输入类型而异,并且需要比仅仅使用v-model快捷方式更多的代码。 - 让我们通过在
onSubmit()方法中记录提交的数据的值来测试v-model的使用。在组件中,数据属性使用this关键字访问。因此,我们使用this.label访问label字段。更新您的onSubmit()方法,使其看起来像这样。jsmethods: { onSubmit() { console.log('Label value: ', this.label); } }, - 现在返回运行的应用程序,在
<input>字段中添加一些文本,然后点击“添加”按钮。您应该会在控制台中看到您输入的值,例如Label value: My value
使用修飾符更改 v-model 行為
与事件修饰符类似,我们也可以添加修饰符来更改 v-model 的行为。在我们的案例中,有两个值得考虑。第一个是 .trim,它将删除输入之前或之后的空格。我们可以将修饰符添加到 v-model 语句中,如下所示:v-model.trim="label"。
我们应该考虑的第二个修饰符称为 .lazy。此修饰符更改了 v-model 为文本输入同步值的时间。如前所述,v-model 同步通过使用事件来更新变量。对于文本输入,这是使用 input 事件 完成的。通常,这意味着 Vue 会在每次按键后同步数据。.lazy 修饰符会导致 v-model 使用 change 事件。这意味着 Vue 只有在输入失去焦点或提交表单时才会同步数据。对于我们的目的,这是更合理的,因为我们只需要最终数据。
要同时使用 .lazy 修饰符和 .trim 修饰符,我们可以将它们链接起来,例如 v-model.lazy.trim="label"。
更新您的 v-model 属性以链接 lazy 和 trim,如上所示,然后再次测试您的应用程序。例如,尝试提交一个两端都有空格的值。
使用自定義事件將數據傳遞給父級
我们现在非常接近能够将新的待办事项添加到我们的列表中。接下来,我们需要能够将新创建的待办事项传递给 App 组件。为此,我们可以让 ToDoForm 发出一个传递数据的自定义事件,并让 App 监听它。这与 HTML 元素上的本机事件非常相似:子组件可以发出一个事件,该事件可以通过 v-on 监听。
在我们的 ToDoForm 的 onSubmit 事件处理程序中,让我们添加一个 todo-added 事件。自定义事件的发出方式如下:this.$emit("event-name")。重要的是要知道,事件处理程序区分大小写,并且不能包含空格。Vue 模板也会转换为小写,这意味着 Vue 模板无法监听以大写字母命名的事件。
- 将
onSubmit()方法中的console.log()替换为以下内容jsthis.$emit("todo-added"); - 接下来,返回
App.vue并将一个methods属性添加到你的组件对象中,其中包含一个addToDo()方法,如下所示。目前,这个方法只需将To-do added日志记录到控制台中。jsexport default { name: "app", components: { ToDoItem, ToDoForm, }, data() { return { ToDoItems: [ { id: "todo-" + nanoid(), label: "Learn Vue", done: false }, { id: "todo-" + nanoid(), label: "Create a Vue project with the CLI", done: true, }, { id: "todo-" + nanoid(), label: "Have fun", done: true }, { id: "todo-" + nanoid(), label: "Create a to-do list", done: false, }, ], }; }, methods: { addToDo() { console.log("To-do added"); }, }, }; - 接下来,将一个针对
todo-added事件的事件监听器添加到<to-do-form></to-do-form>中,当事件触发时,它会调用addToDo()方法。使用@简写,监听器看起来像这样:@todo-added="addToDo"html<to-do-form @todo-added="addToDo"></to-do-form> - 当你提交
ToDoForm时,你应该会看到addToDo()方法中的控制台日志。这很好,但我们还没有将任何数据传回App.vue组件。我们可以通过在ToDoForm组件中的this.$emit()函数中传递额外的参数来做到这一点。在本例中,当我们触发事件时,我们希望将label数据一并传递过去。这可以通过将你想要传递的数据作为$emit()方法中的另一个参数来实现:this.$emit("todo-added", this.label)。这类似于原生 JavaScript 事件包含数据的方式,只是自定义 Vue 事件默认情况下不包含任何事件对象。这意味着发出的事件将直接与你提交的任何对象匹配。因此,在我们的例子中,我们的事件对象只是一个字符串。更新你的onSubmit()方法,如下所示jsonSubmit() { this.$emit('todo-added', this.label) } - 为了实际在
App.vue中获取此数据,我们需要在addToDo()方法中添加一个参数,该参数包含新待办事项的label。返回App.vue并立即更新它jsmethods: { addToDo(toDoLabel) { console.log('To-do added:', toDoLabel); } }
如果你再次测试你的表单,你将看到你在提交时在控制台中记录的任何文本。Vue 自动将 this.$emit() 中事件名称之后的参数传递到你的事件处理程序。
將新的待辦事項添加到我們的數據中
现在我们已经获得了 ToDoForm 中的数据,并将其提供给 App.vue,我们需要向 ToDoItems 数组中添加一个代表它的项目。这可以通过将一个包含我们新数据的待办事项对象推送到数组中来实现。
- 更新你的
addToDo()方法,如下所示jsaddToDo(toDoLabel) { this.ToDoItems.push({id: "todo-" + nanoid(), label: toDoLabel, done: false}); } - 再次尝试测试你的表单,你应该会看到新的待办事项被追加到列表的末尾。
- 在我们继续之前,让我们再做进一步的改进。如果你在输入为空时提交表单,则仍然会将没有文本的待办事项添加到列表中。为了解决这个问题,我们可以防止在名称为空时触发 todo-added 事件。由于名称已经被
.trim修饰符修剪过,我们只需要测试空字符串。返回你的ToDoForm组件,并更新onSubmit()方法,如下所示。如果标签值为空,则不要发出todo-added事件。jsonSubmit() { if (this.label === "") { return; } this.$emit('todo-added', this.label); } - 再次尝试你的表单。现在你将无法将空项目添加到待办事项列表中。
使用 v-model 更新輸入值
在我们的 ToDoForm 组件中,还有一件事需要修复,那就是提交后,<input> 仍然包含旧值。但要修复这一点很容易,因为我们使用 v-model 将数据绑定到 ToDoForm 中的 <input>,如果我们将 name 参数设置为等于空字符串,则输入也将更新。
将你的 ToDoForm 组件的 onSubmit() 方法更新为此方法
onSubmit() {
if (this.label === "") {
return;
}
this.$emit('todo-added', this.label);
this.label = "";
}
现在,当你点击 "添加" 按钮时,"new-todo-input" 将会清除自身。