Vue.js: data、v-model 與雙向綁定

Vue

如何利用 data 與 v-model 實現雙向綁定?原理、語法與表單元件範例。

data

vue instance 可傳入選項物件,其中可設定 data(資料)。

data 是用來

資料型別

data 可以是 object 或 function,但元件(component)的 data 只能是 function,這是因為元件內各自擁有自己的 data,而非共用的關係。

原理

在 observer 中,data 透過Object.defineProperty()為元件內屬性重新定義gettersetter method。當 data 被修改時,會透過setter通知變化,觸發 watcher 重新計算、更新與渲染 DOM element。

語法

設定 data,如下所示。

<div id="app">
  ${ message }
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    message: 'Hello World!',
  },
});

// 由於部落格會把使用雙花括號的內容吃掉,所以設定 delimiters 以顯示完整程式碼。

元件的 data 只能是 function。

var Component = Vue.extend({
  data: function() {
    return {
      msg: 'Hello World!',
    };
  },
});

打開瀏覽器會看到

Vue.js: data 與 v-model - Hello World

由於 vue instance 重新設定了gettersetter方法,因此 vm.$data.message 完全等於 vm.message

vm.$data.message === vm.message; // true

注意

v-model

v-model 是綁定在表單元件或自訂元件上,為實現雙向綁定用的。表單元件像是<input><select><textarea>,分別說明如下。

Input Text 單行文字

雙向綁定 - Input Text 單行文字

<div id="app">
  <input type="text" v-model="message" />
  <div>${ message }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    message: 'Hello World!',
  },
});

Textarea 多行文字

雙向綁定 - Textarea 多行文字

<div id="app">
  <textarea v-model="message"/></textarea>
  <div>${ message }</div>
</div>

js 程式碼同上-Input Text 單行文字

Checkbox 複選框

使用v-model 綁定 toggle 的值:當 toggle 為 true,勾選此項目;當 toggle 為 false,不勾選此項目。

一開始先設定 toggle 為 false,不勾選此項目。

Checkbox 複選框 - 不勾選此項目

<div id="app">
  <input type="checkbox" v-model="toggle" /><label>我是複選框</label>
  toggle = ${ toggle }
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    toggle: false,
  },
});

再來,可在開發者工具的 console tab 上操作 toggle 為 true,勾選此項目。

Checkbox 複選框 - 勾選此項目

toggle = true;

或依據給訂的值決定是否勾選,若 toggle 為 1 則勾選,若 toggle 為 2 則不勾選。

<div id="app">
  <input type="checkbox" v-model="toggle" :true-value="1" :false-value="2" />
  <label>我是複選框</label>
  toggle = ${ toggle }
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    toggle: 0,
  },
});

設定 toggle 為 1,勾選此項目。

Checkbox 複選框 - 勾選此項目

vm.toggle = 1;

設定 toggle 為 2,不勾選此項目。

Checkbox 複選框 - 不勾選此項目

vm.toggle = 2;

將多個 checkbox 綁定到同一個群組。在這裡設定 group 為一個陣列,裡面存放勾選選項的 value 字串。例如,若 group 為 ["1", "2"],則勾選第一個和第二個複選框。

Checkbox 複選框 - 將多個 checkbox 綁定到同一個群組

<div id="app">
  <input type="checkbox" v-model="group" value="1" /><label>我是複選框 1</label>

  <input type="checkbox" v-model="group" value="2" /><label>我是複選框 2</label>

  <input type="checkbox" v-model="group" value="3" /><label>我是複選框 3</label>

  <div>group = ${ group }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    group: [],
  },
});

Radio Button 單選按鈕

Radio Button 單選按鈕

<div id="app">
  <input type="radio" v-model="selected" value="我是單選按鈕 1" /><label>我是單選按鈕 1</label>

  <input type="radio" v-model="selected" value="我是單選按鈕 2" /><label>我是單選按鈕 2</label>

  <input type="radio" v-model="selected" value="我是單選按鈕 3" /><label>我是單選按鈕 3</label>

  <div>selected = ${ selected }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    selected: '尚未選擇任何一項',
  },
});

Select 下拉選單

單選。

Select 下拉選單

<div id="app">
  <select v-model="selected">
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <div>selected = ${ selected }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    selected: null,
  },
});

多選,在<select>加上 multiple 屬性。

Select 下拉選單 多選

<div id="app">
  <select v-model="selected" multiple>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <div>selected = ${ selected }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    selected: [],
  },
});

選定選項 B。

Select 下拉選單 多選

vm.selected = ['B'];

Modifiers 修飾符

.lazy

原本雙向綁定的更新方式是以 input 事件監聽,亦即資料變動即更新,但使用.lazy會改用 change 事件監聽,event trigger 才更新。

如下所示,更改 input 內的值並不會馬上變更 model 的資料,而是等到滑鼠移到輸入框外,觸發 change 事件才更新。

v-model modifiers - .lazy

<div id="app">
  <input v-model.lazy="message" type="text" />
  <div>${ message }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    message: 'Hello World!',
  },
});

對照一下前面的例子,若無.lazy,是以 input 事件監聽,資料改變就會更新。

雙向綁定 - Input Text 單行文字

.number

將字串轉為數字。

如果沒有強制轉換,我們在v-model所得到的值的資料型態是 string。

如下所示,在輸入框輸入數字,然後印出這個數字的資料型別。

v-model 的資料型態預設為字串

<div id="app">
  <input v-model="count" />
  <div>${ count }</div>
  <div>${ type }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    count: 0,
    type: '',
  },
  watch: {
    count: function(val) {
      this.type = typeof val;
    },
  },
});

使用.number強制轉為數字。

如下所示,輸入 1 後,得到的資料型別是 number,而非 string。

v-model 的資料型態使用 .number強制轉為數字

<div id="app">
  <input v-model.number="count" />
  <div>${ count }</div>
  <div>${ type }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    count: 0,
    type: '',
  },
  watch: {
    count: function(val) {
      this.type = typeof val;
    },
  },
});

.trim

去除首尾空白。

如下所示,輸入前面有空白的字串,滑鼠移開後不會去除空白。

v-model 尚未去除空白

<div id="app">
  <input v-model="message" />
  <div>${ message }</div>
</div>
var vm = new Vue({
  el: '#app',
  delimiters: ['${', '}'],
  data: {
    message: 'Hello World!',
    type: '',
  },
});

v-model 使用.trim去除空白。

如下所示,輸入前面有空白的字串,滑鼠移開後會去除空白。

v-model 使用 .trim 去除空白

<div id="app">
  <input v-model.trim="message" />
  <div>${ message }</div>
</div>

關於自定義元件與v-model的狀況可參考這篇-使用自定義事件的表單輸入元件

以上參考 Form Input Bindings — Vue.js


vue.js two-way binding vue.js directives v-model 雙向綁定