新增新的待辦事項表單:Vue 事件、方法和模型

現在我們已經有了示例資料,以及一個迴圈,它獲取每個資料並將其渲染到應用中的 ToDoItem 中。我們接下來真正需要的是讓使用者能夠將他們自己的待辦事項輸入到應用程式中,為此我們需要一個文字 <input>,一個當資料提交時觸發的事件,一個在提交時觸發的新增資料並重新渲染列表的方法,以及一個控制資料的模型。這就是我們將在本文中討論的內容。

先決條件

熟悉核心 HTMLCSSJavaScript 語言,瞭解 終端/命令列

Vue 元件以 JavaScript 物件(管理應用程式資料)和基於 HTML 的模板語法(對映到底層 DOM 結構)的組合形式編寫。要進行安裝,並使用 Vue 的一些更高階功能(如單檔案元件或渲染函式),您需要一個安裝了 node + npm 的終端。

目標 瞭解如何在 Vue 中處理表單,以及相關的事件、模型和方法。

建立新的待辦事項表單

我們現在有一個顯示待辦事項列表的應用程式。但是,我們無法更新專案列表,除非手動更改程式碼!讓我們解決這個問題。讓我們建立一個新的元件,允許我們新增新的待辦事項。

  1. 在您的元件資料夾中,建立一個名為 ToDoForm.vue 的新檔案。
  2. 新增一個空白的 <template> 和一個 <script> 標籤,就像以前一樣。
    html
    <template></template>
    
    <script>
      export default {};
    </script>
    
  3. 讓我們新增一個 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 的標籤)。
  4. 讓我們將此元件載入到我們的應用程式中。返回到 App.vue,並在之前的語句下方新增以下 import 語句,位於您的 <script> 元素中。
    js
    import ToDoForm from "./components/ToDoForm";
    
  5. 您還需要在 App 元件中註冊新的元件 — 更新元件物件的 components 屬性,使其看起來像這樣。
    js
    components: {
      ToDoItem, ToDoForm,
    }
    
  6. 最後,在本節中,透過將 <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>
    

現在,當您檢視執行的網站時,您應該會看到新顯示的表單。

Our todo list app rendered with a text input to enter new todos

如果您填寫它並點選“新增”按鈕,該頁面將把表單釋出回伺服器,但這並不是我們真正想要的。我們實際上想要做的是在 submit 事件 上執行一個方法,該方法將新的待辦事項新增到 App 內定義的 ToDoItem 資料列表中。為此,我們需要向元件例項新增一個方法。

建立方法並使用 v-on 將其繫結到事件

要使一個方法對 ToDoForm 元件可用,我們需要將其新增到元件物件中,這在元件物件的 methods 屬性中完成,它位於 data()props 等的位置。methods 屬性儲存我們可能需要在元件中呼叫的任何方法。當引用時,方法會完全執行,因此不要將其用於在模板中顯示資訊。對於顯示來自計算的資料,您應該使用 computed 屬性,我們將在稍後介紹。

  1. 在此元件中,我們需要向 ToDoForm 元件物件的 methods 屬性中新增一個 onSubmit() 方法。我們將使用它來處理提交操作。按如下方式新增它。
    js
    export default {
      methods: {
        onSubmit() {
          console.log("form submitted");
        },
      },
    };
    
  2. 接下來,我們需要將該方法繫結到 <form> 元素的 submit 事件處理程式。與 Vue 如何使用 v-bind 語法繫結屬性非常類似,Vue 還有一個用於事件處理的特殊指令:v-onv-on 指令透過 v-on:event="method" 語法工作。並且與 v-bind 類似,也有一種簡寫語法:@event="method"。為了保持一致性,我們將在這裡使用簡寫語法。將 submit 處理程式新增到您的 <form> 元素中,如下所示。
    html
    <form @submit="onSubmit"></form>
    
  3. 當您執行它時,應用程式仍然將資料釋出到伺服器,導致重新整理。由於我們是在客戶端執行所有處理,因此沒有伺服器來處理回發。我們還會在頁面重新整理時丟失所有本地狀態。要防止瀏覽器釋出到伺服器,我們需要在頁面冒泡時停止事件的預設操作(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 屬性來跟蹤待辦事項的值。

  1. ToDoForm 元件物件新增一個 data() 方法,該方法返回一個 label 欄位。我們可以將 label 的初始值設定為一個空字串。您的元件物件現在應該看起來像這樣。
    js
    export default {
      methods: {
        onSubmit() {
          console.log("form submitted");
        },
      },
      data() {
        return {
          label: "",
        };
      },
    };
    
  2. 我們現在需要一種方法將 new-todo-input 元素的欄位的值附加到 label 欄位。Vue 專門為此提供了一個指令:v-modelv-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 快捷方式更多的程式碼。

  3. 讓我們透過在 onSubmit() 方法中記錄提交的資料的值來測試 v-model 的使用。在元件中,資料屬性使用 this 關鍵字訪問。因此,我們使用 this.label 訪問 label 欄位。更新您的 onSubmit() 方法,使其看起來像這樣。
    js
    methods: {
      onSubmit() {
        console.log('Label value: ', this.label);
      }
    },
    
  4. 現在返回執行的應用程式,在 <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 屬性以連結 lazytrim,如上所示,然後再次測試您的應用程式。例如,嘗試提交一個兩端都有空格的值。

使用自定義事件將資料傳遞給父級

我們現在非常接近能夠將新的待辦事項新增到我們的列表中。接下來,我們需要能夠將新建立的待辦事項傳遞給 App 元件。為此,我們可以讓 ToDoForm 發出一個傳遞資料的自定義事件,並讓 App 監聽它。這與 HTML 元素上的本機事件非常相似:子元件可以發出一個事件,該事件可以透過 v-on 監聽。

在我們的 ToDoFormonSubmit 事件處理程式中,讓我們新增一個 todo-added 事件。自定義事件的發出方式如下:this.$emit("event-name")。重要的是要知道,事件處理程式區分大小寫,並且不能包含空格。Vue 模板也會轉換為小寫,這意味著 Vue 模板無法監聽以大寫字母命名的事件。

  1. onSubmit() 方法中的 console.log() 替換為以下內容
    js
    this.$emit("todo-added");
    
  2. 接下來,返回 App.vue 並將一個 methods 屬性新增到你的元件物件中,其中包含一個 addToDo() 方法,如下所示。目前,這個方法只需將 To-do added 日誌記錄到控制檯中。
    js
    export 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");
        },
      },
    };
    
  3. 接下來,將一個針對 todo-added 事件的事件監聽器新增到 <to-do-form></to-do-form> 中,當事件觸發時,它會呼叫 addToDo() 方法。使用 @ 簡寫,監聽器看起來像這樣:@todo-added="addToDo"
    html
    <to-do-form @todo-added="addToDo"></to-do-form>
    
  4. 當你提交 ToDoForm 時,你應該會看到 addToDo() 方法中的控制檯日誌。這很好,但我們還沒有將任何資料傳回 App.vue 元件。我們可以透過在 ToDoForm 元件中的 this.$emit() 函式中傳遞額外的引數來做到這一點。在本例中,當我們觸發事件時,我們希望將 label 資料一併傳遞過去。這可以透過將你想要傳遞的資料作為 $emit() 方法中的另一個引數來實現:this.$emit("todo-added", this.label)。這類似於原生 JavaScript 事件包含資料的方式,只是自定義 Vue 事件預設情況下不包含任何事件物件。這意味著發出的事件將直接與你提交的任何物件匹配。因此,在我們的例子中,我們的事件物件只是一個字串。更新你的 onSubmit() 方法,如下所示
    js
    onSubmit() {
      this.$emit('todo-added', this.label)
    }
    
  5. 為了實際在 App.vue 中獲取此資料,我們需要在 addToDo() 方法中新增一個引數,該引數包含新待辦事項的 label。返回 App.vue 並立即更新它
    js
    methods: {
      addToDo(toDoLabel) {
        console.log('To-do added:', toDoLabel);
      }
    }
    

如果你再次測試你的表單,你將看到你在提交時在控制檯中記錄的任何文字。Vue 自動將 this.$emit() 中事件名稱之後的引數傳遞到你的事件處理程式。

將新的待辦事項新增到我們的資料中

現在我們已經獲得了 ToDoForm 中的資料,並將其提供給 App.vue,我們需要向 ToDoItems 陣列中新增一個代表它的專案。這可以透過將一個包含我們新資料的待辦事項物件推送到陣列中來實現。

  1. 更新你的 addToDo() 方法,如下所示
    js
    addToDo(toDoLabel) {
      this.ToDoItems.push({id: "todo-" + nanoid(), label: toDoLabel, done: false});
    }
    
  2. 再次嘗試測試你的表單,你應該會看到新的待辦事項被追加到列表的末尾。
  3. 在我們繼續之前,讓我們再做進一步的改進。如果你在輸入為空時提交表單,則仍然會將沒有文字的待辦事項新增到列表中。為了解決這個問題,我們可以防止在名稱為空時觸發 todo-added 事件。由於名稱已經被 .trim 修飾符修剪過,我們只需要測試空字串。返回你的 ToDoForm 元件,並更新 onSubmit() 方法,如下所示。如果標籤值為空,則不要發出 todo-added 事件。
    js
    onSubmit() {
      if (this.label === "") {
        return;
      }
      this.$emit('todo-added', this.label);
    }
    
  4. 再次嘗試你的表單。現在你將無法將空專案新增到待辦事項列表中。

Our todo list app rendered with a text input to enter new todos

使用 v-model 更新輸入值

在我們的 ToDoForm 元件中,還有一件事需要修復,那就是提交後,<input> 仍然包含舊值。但要修復這一點很容易,因為我們使用 v-model 將資料繫結到 ToDoForm 中的 <input>,如果我們將 name 引數設定為等於空字串,則輸入也將更新。

將你的 ToDoForm 元件的 onSubmit() 方法更新為此方法

js
onSubmit() {
  if (this.label === "") {
    return;
  }
  this.$emit('todo-added', this.label);
  this.label = "";
}

現在,當你點選 "新增" 按鈕時,"new-todo-input" 將會清除自身。

總結

太好了。現在我們可以向我們的表單新增待辦事項了!我們的應用程式現在開始變得互動起來,但一個問題是,我們到目前為止完全忽略了它的外觀和感覺。在下一篇文章中,我們將集中精力解決這個問題,看看 Vue 提供的幾種樣式化元件的方式。