iT邦幫忙

7

[筆記][Vue.js]打開Vue.js世界的大門(4)-讓v-model雙向綁定抓住你的資料

HI!記得我們在第一篇的時候有提過如何將Vue.js的資料綁定到View上面嗎?如果忘記的話可以看一下以下最簡單的範例:
HTML

<input id="name" :value="text" >

JavaScript

let name = new Vue({
    el:'#name',
    data:{
        text:'神Q超人',
    },
})

透過指定HTML的元件來創建一個Vue物件,並在物件中用data屬性傳入資料物件,這是一個Vue.js對View的單向綁定,而Vue.js還有提供雙向綁定!我們今天就來提一下要如何做到雙向綁定!

v-model

在Vue.js中有個v-model屬性可以做到這件事情,他可以對inputtextareaselectcheckboxcheckbox這堆全部讓使用者輸入的表單做雙向的綁定,主要是透過事件去監聽使用者輸入的事件來更新我們一開始給的,感覺是不是很厲害?讓我們動手做看看吧!

input

像第一篇說的,Vue.js的用法都非常直覺,感覺很難的v-model也是,只需要將v-model當做屬性加到input裡面就可以了!
HTML

<div id="name">
    <!--為input設定一個v-model屬性,並將綁定的資料設定為text-->
    <input v-model="text" :value="text">
    <div>您的姓名是:{{text}}</div>
</div>

JavaScript

//這裡把資料拆出來
let nameData = {
    text : '神Q超人'
}

let name = new Vue({
    el:'#name',
    data:nameData,
})

在HTML的部分,我們將text這個資料綁定在三個地方,分別是input的預設值、div的內容和我們今天提到的v-model,因為v-model的關係,他在監聽到input的資料如果改變,會即時更新text裡面的值,而又因為div內綁定了text的資料,所以也會同步更新成新值,就像下方的操作:
https://ithelp.ithome.com.tw/upload/images/20180820/2010693573Fyf9wG4U.jpg
很簡單吧!那讓我們繼續說下去!

textarea

這個表單就和input一樣,不同的是文字可以在textarea內折行,而厲害的是,就算折行Vue.js也不會忘記他: 
HTML

<div id="introduce">
    <textarea v-model="text" :value="text"></textarea>
    <div>您的自我介紹內容:</div>
    <pre>{{text}}</pre>
</div>

JavaScript

let introduceData = {
    text : '自我介紹內容'
}

let introduce = new Vue({
    el:'#introduce',
    data:introduceData,
})

https://ithelp.ithome.com.tw/upload/images/20180820/20106935LjFycaKMg8.jpg
這邊題外話一下,上面我把text放到一個pre標籤中,這個<pre>我之前發現的神奇標籤,在他裡面會保留textarea內輸入的換行,這是我的秘密武器XD

select

select比較特別,我們這裡應用上一篇講的迴圈來填入select的選項,然後像上方一樣,在select中增加v-model去綁定數據。

不一樣的是,因為選項是動態產生的,資料會存在v-for="list in lists"list中,但實際上的data內並沒有list這個屬性,所以必須再增加一個selectNamev-model綁定數據,這麼一來,只要select的選項改變,list內的item就會把目前綁定的value值(下方程式碼中的:value="list.item")寫給selectName,而後方的資料變動,也就會同時更新View,可以看以下範例:

<div id="gender">
    <select v-model="selectName">
        <option v-for="list in lists" :value="list.item">{{list.item}}</option>
    </select>
    <div>選擇的性別是:{{selectName}}</div>
</div>
let genderData = {
    selectName : '',
    lists : [
        {val:"M",item:'男'},
        {val:"W",item:'女'},
    ]
}

let gender = new Vue({
    el:'#gender',
    data:genderData,
})

https://ithelp.ithome.com.tw/upload/images/20180820/20106935hYriuqNtP7.jpg
看到上方的結果,我們確實做到了select的雙向綁定,但也許會有個問題,如果我要同時取得optionvaluetext呢?就目前看來似乎只能綁定一個,所以這時候我們要利用事件綁定方法的@change來處理這件事情!
HTML

<div id="gender">
    <!--這裡把v-model先拿掉,要留著也可以,增加了@change事件-->
    <select id="s_gender" :value="selectVal" @change="updateVal">
      <option v-for="list in lists" :value="list.val" >{{list.item}}</option>
    </select>
    <div>選擇的性別是:{{selectName}}</div>
    <div>該性別的值是:{{selectVal}}</div>
</div>

JavaScript

let genderData = {
    selectName : '',
    selectVal : '',
    lists : [
        {val:"M",item:'男'},
        {val:"W",item:'女'},
    ]
}
let genderMethods = {
    //將目前該性別選單的value和name寫入genderData的selectName及selectVal
    updateVal : () =>{
        let obj = document.getElementById('s_gender')
        //取得值
        genderData.selectVal = obj.value;
        //取得目前選擇選項的文字
        genderData.selectName = obj.options[obj.selectedIndex].text;
      
    }
}
let gender = new Vue({
    el:'#gender',
    data:genderData,
    methods:genderMethods,
})

https://ithelp.ithome.com.tw/upload/images/20180820/20106935j3djAgFq80.jpg
結果會將兩個值都帶出來,不過上面的方法看起來有點複雜,其實只是土法煉鋼用change事件去更新genderData內屬性的值,並藉由單像綁定的特性更動前面的View,不過用這個方法就等於和沒v-model的做法一樣,不曉得有沒有更好的方法,如果有其他做法還麻煩各位大大教我一下,謝謝!

select的multiple版本

下拉選單有兩個版本,一種是上面提到的單選的,另一種是複選的,其實我也不曉得從什麼時候,只要在select中加入multiple屬性,他自然就會變成複選的版本,Vue.js在處理複選的資料時,他會把所有選擇的資料,不管幾個都放進陣列再寫回後方的data中,我們用剛剛的範例直接看看結果吧!
HTML

<div id="gender">
    <!--在select屬性中加入multiple設定為複選-->
    <select v-model="selectName" multiple >
        <option v-for="list in lists" :value="list.item">{{list.item}}</option>
    </select>
    <div>選擇的性別是:{{selectName}}</div>
</div>

JavaScript

let genderData = {
    selectName : '',
    lists : [
        {val:"M",item:'男'},
        {val:"W",item:'女'},
    ]
}

let gender = new Vue({
    el:'#gender',
    data:genderData,
})

以上其實都沒有改變,就只是在select中加入了multiple屬性,不過這個在實務上似乎沒有那麼常見,結果會像下方,當我選擇一個或以上的值的時候,綁定的資料會變成陣列寫回!
https://ithelp.ithome.com.tw/upload/images/20180820/20106935NSiqhie1sF.jpg

checkbox

看到這裡,應該就會輕鬆不少了,因為剩下的單選框和複選框的用法就和下拉選單差不多,直接來看看吧(原諒我偷懶直接把上面的下拉選單改成複選框XD)!
HTML

<div id="gender">
    <div>
      <span v-for="list in lists">
        <input type="checkbox" :value="list.item" v-model="checkedNames"/>
        <label>{{list.item}}</label>
      </span>
    </div>
    <div>選擇的性別是:{{checkedNames}}</div>
</div>

JavaScript

let genderData = {
    //這裡綁定的值要是陣列
    checkedNames : [],
    lists : [
        {val:"M",item:'男'},
        {val:"W",item:'女'},
    ]
}

let gender = new Vue({
    el:'#gender',
    data:genderData,
})

https://ithelp.ithome.com.tw/upload/images/20180820/2010693576yd59c1aX.jpg
結果應該是和下拉選單的multiple版本是一樣的,但是有個卡了我非常久的地方XD,如果是check的話一開始由v-modle綁定的資料應該要是陣列型態,也就是說checkedNames的初始設置checkedNames:''這樣是不行的,他必須要是checkedNames:[]才可以,這點用在checkbox的時候要注意一下!

radio

終於來到最後了XD,這裡是單選框,也就是很單純的單一值,沒有多選就沒有陣列,那來看看壓軸的範例吧(如果你發現下面的範例和上面根本一模一樣,那絕對是錯覺XD)!
HTML

<div id="gender">
    <div>
      <span v-for="list in lists">
        <input type="radio" :value="list.item" v-model="checkedNames"/>
        <label>{{list.item}}</label>
      </span>
    </div>
    <div>選擇的性別是:{{checkedNames}}</div>
</div>

JavaScript

let genderData = {
    checkedNames : '',
    lists : [
        {val:"M",item:'男'},
        {val:"W",item:'女'},
    ]
}

let gender = new Vue({
    el:'#gender',
    data:genderData,
})

做到了這裡,各位應該腦子裡都會有畫面大概會呈現什麼結果了吧!那我們一起來見證是不是和想像中一樣!
https://ithelp.ithome.com.tw/upload/images/20180820/2010693538JxZHzIr3.jpg
沒錯!所以其實看似很難的雙向綁定,在Vue.js中也變的簡單起來了,當然還是有些時候我們必須手動去取值給data,就像上方下拉選單同時取valuetext的情況,不過如果是我理解錯誤,拜託各位大大一定要告訴我!不想活在錯誤中啊XD,另外關於v-model還有幾點要額外提的部分:

1. .lazy

v-modle中,我們使用雙向綁定,不論是input或是select資料都會即時的綁定,但是當你使用input又不想讓他即時綁定資料,可以加上.lazy屬性,他就像一般的onchange一樣,只有在該input失去焦點的時候才會重新綁定資料!
HTML

<div id="name">
    <input v-model.lazy="text" :value="text">
    <div>您的姓名是:{{text}}</div>
</div>

JavaScript

let nameData = {
    text : '神Q超人'
}

let name = new Vue({
    el:'#name',
    data:nameData,
})

https://ithelp.ithome.com.tw/upload/images/20180821/20106935Zz2rkTsxgq.jpg
這麼一來就會如畫面上,當游標還停留在input時,Vue.js就不會進行資料的綁定,只有在焦點離開input的時候才會再綁定data,當然如果是像select之類的表單,在選擇的時候就等於離開焦點,所以也感受不到.lazy的效果。

2. .number

這一個.number則是會在type="number"時使用,為了避免有文字輸入的情況,他會只容許數字進行綁定,例如:
HTML

<div id="age">
    <input v-model.number="text" :value="text" type="number">
    <div>您的年齡是:{{text}}</div>
</div>

JavaScript

let ageData = {
    text : '神Q超人'
}

let age = new Vue({
    el:'#age',
    data:ageData,
})

https://ithelp.ithome.com.tw/upload/images/20180821/2010693546IjPsSFxo.jpg
可以看到,就算我一開始的data是中文,他也不會進行資料綁定到inputvalue當中,一直到我把他的value更改成數字,才會進行綁定。

3. .trim

這個屬性就單純多了,會在綁定資料的時候自動清除前後的空白!
HTML

<div id="name">
    <input v-model.trim="text" :value="text">
    <div>您的姓名是:{{text}}</div>
</div>

JavaScript

let nameData = {
    text : '神Q超人'
}

let name = new Vue({
    el:'#name',
    data:nameData,
})

https://ithelp.ithome.com.tw/upload/images/20180821/20106935SAeGmcrv6j.jpg
如上圖,當在文字的前後不論輸入多少空白都不會被綁定到data中,不過文字與文字之間的空白則會保留,另外當焦點移開input時,也會重新綁定成移除字串頭尾空白後的資料!

補充說明(感謝fysh711426大大在此篇文章中的補充)

watch的運作主要是因為v-model幫我們做了兩件事情

  1. 使用v-bind單向綁定資料,會因為每次綁定值的變動同步更新到View上。
  2. 使用v-on:input去偵測每次值的變動,觸發更新v-bind綁定的值。

所以以下兩種寫法是一樣的:
v-model

<input v-model="text">

v-bind&v-on:input

<input
  v-bind:value="text"
  v-on:input="text = $event.target.value">

參考文章:https://blog.csdn.net/u010320804/article/details/79486034


以上!講了很多關於v-modle的用法和情況,不過這些都只是基本的而已,其實只要看懂一個就能夠應用在其他表單上,在這之後還有更可怕的在等著XD。最後也感謝各位大大的觀看!如果文章中有任何理解錯誤或是不清楚的地方,還請留言告訴我,我會盡快修正的!謝謝大家!/images/emoticon/emoticon41.gif


1 則留言

0
jack1234552000
iT邦新手 5 級 ‧ 2019-08-30 00:55:51

謝謝你的教學 陪伴我第四天
前幾章 我們透過控制台操作或是利用事件直接修改data資料
達到DOM重新繪製

這章簡單講 是做相反的事情
也就是修改畫面上看到的東西(你說的view)
藉此修改data資料 讓畫面上其他地方的資料做變動

目前看到前半段
一年多過去了 你應該也解決了(笑
不知道這算不算一種解決方法
其實就是在綁定的value屬性改成list
讓他可以取得物件的值 就行了

<div id="gender">
    <select v-model="selectName">
        <option v-for="list in lists" :value="list">{{list.item}}</option>
    </select>
    <div>選擇的性別是:{{selectName.item}}</div>
    <div>性別的值是:{{selectName.val}}</div>
</div>

我先看到前半段
今天有點累了 明天接續後半段

我要留言

立即登入留言