Quasar 學習筆記 Ep 06 - Quasar 資料綁定

 Quasar 資料綁定

這一篇是非常重要的分享,因為會談到 Vue 最重要的特色,資料綁定。我們可以這樣理解,Vue的網頁檔是寫在 .vue 裡面的,內含了兩個重要的部分,一個是網頁的內容,一個是程式的內容 (內含的變數),一般而言,在網頁第一次渲染時,程式中的變數會顯示在網頁上,但是當我們在網頁中修改這些變數值時,就需要一個事件引發讓更改的資料同步到程式的變數中,反向亦然。

而Vue 可以將網頁的顯示值與程式的變數值雙向綁定,程式設計師就再也不需要額外處理,兩個地方的資料會永遠保持同步。這就是所謂的MVVM開發框架。

透過這篇文章可以學到:

  1. <input> 文字框的處理
  2. v-model 雙向綁定
  3. @ 事件處理
  4. 程式中的方法(Method)介紹
  5. Quasar 表單元件 (QField)

先回顧 TotalPrice.vue 程式碼,網頁中的變數如 {{price}} 等,只是顯示這個變數,並沒有綁定。我們將做一些修改,事實上 price, amount 是可以在網頁上讓使用者輸入的,所以馬上就要談到 <input> 標籤。

<template>
  <p>漢堡單價 {{price}} 元,買 {{amount}} 個,共 {{total}} 元。</p>
</template>

<script>
export default {
  name: 'TotalPrice',
  props: {
    price: { type: Number, default: 70 },
    amount: { type: Number, default: 0 }
  },
  data() {
    return {
      total: this.price * this.amount
    }
  }
}
</script>

<style scoped>
p {
  margin: 5px;
  color: rgb(196, 16, 121);
  font-weight: bold;
}
</style>

新增文字框

為了不影響之前所寫的程式,我們新增一個 TotalPrice2.vue 元件檔。我們要做的事情是,將 price 和 amount 設計成文字框,Template 部分改寫如下:

<template>
  <p>漢堡單價 <input v-model="price"> 元,
     買 <input v-model="amount"> 個,
     共 {{total}} 元。</p>
</template>

在這裡,我們利用了一個 Vue 專屬指令,v-model,目的就是把程式中的 price 變數,跟這個文字框綁定。關於 v- 指令,是Vue 所專屬,當我們利用 IntelliSense 輸入時,就會出現所有 v- 指令的提示,如下圖。我不會一一介紹,用到的時候才會說明。

馬上就遇到問題了,程式才剛寫完,就出現紅線,警告語法有問題,如下圖:


這要說到 props 的用途,它是用來幫助網頁上的標籤將參數值傳遞給程式的,並不允許我們在程式中對它們做其他的操作,就像是 Read Only 的變數。既然 props 無法與 <input> 這個 element 綁定,我們就要使用 data() 來達成。

作法是,先保留 props 的設定,在 data() 中宣告另外兩個變數,myprice 與 myamount,如下:

<template>
  <p>漢堡單價 <input v-model="myprice"> 元,
<input v-model="myamount"> 個,
共 {{total}} 元。</p>
</template>

<script>
export default {
  name: 'TotalPrice2',
  props: {
    price: { type: Number, default: 70 },
    amount: { type: Number, default: 0 }
  },
  data() {
    return {
      myprice: this.price,
      myamount: this.amount,
      total: this.price * this.amount
    }
  }
}
</script>

這樣紅色底線就消失了,代表語法正確。接下來我們在 About.vue 中呼叫 <total-price-2>。

    <total-price price = "50" amount = "12"/>
    <total-price-2 price = "150" amount = "120"/>

看似完美,我們看看執行結果:

漢堡單價 150 元,購買 120 個,總價 18000 元。在這個頁面中,我們綁定了 price 與 amount 這兩個變數,當您在文字框中將單價改為 100 時,被綁定的 myprice 就會變成 100,同理,myamount 也一樣。

想要確認綁定的效果,我們可以多加兩個變數進去,自行測試便知:

<template>
  <p>漢堡單價 <input v-model="myprice"> 元,
     買 <input v-model="myamount"> 個,
     共 {{total}} 元(@單價:{{myprice}}*數量:{{myamount}})。</p>
</template>

問題來了,當我們去改變 myprice 與 myamount 的數值時,會發現總價 {{total}} 並不會改變,永遠停留在 18000 元。這是因為,{{total}} 並沒有綁定。

千萬不要認為 data() 應該要處理 {{total}} 的數值改變,試圖用下面的程式寫法:

  data() {
    return {
      myprice: this.price,
      myamount: this.amount,
      total: myprice * myamount, ... (X 錯誤)
    }
  },

由於這段程式碼是宣告並給初始值,用的是冒號而不是等號,我們怎麼可以用兩個正在宣告的變數去宣告另一個變數呢?這樣的寫在語法上不會報錯,但是執行時就會有錯誤,保證網頁出不來。

@change 事件

要解決這個問題,我們必須用 @change 事件來處理。想法很簡單,當 <input> 裡面的值被改變的時候,就會產生一個 @change 事件,然後我們設計一個 onChange() 函數,讓 total 重新運算。

寫程式時可以使用 IntelliSense 來輸入,在 <input ... 最後面輸入 "@",就會跑出一串事件,如下圖:


按下 Tab 鍵選取 @change 之後,再輸入一個 method name: onChange,這裡不用加括號。如下圖所示:

<template>
  <p>漢堡單價 <input v-model="myprice" @change="onChange"> 元,
     買 <input v-model="myamount" @change="onChange"> 個,
     共 {{total}} 元。</p>
</template>

物件中的方法

當我們在 <input> 標籤中加入了 onChange 的當下,onChange() 還不存在,接下來,我們就來加入一個 onChange() 的方法。終於,要談到程式中 methods: 區段的寫法了。

請參考下圖,將 methods: 內容寫在最後面:

<script>
export default {
  name: 'TotalPrice2',
  props: {
    price: { type: Number, default: 70 },
    amount: { type: Number, default: 0 }
  },
  data() {
    return {
      myprice: this.price,
      myamount: this.amount,
      total: this.price * this.amount
    }
  },
  methods: {
    onChange() {
      this.total = this.myprice * this.myamount
    }
  }
}
</script>

首先,定義 onChange() method 的方式跟定義 data() 很像,只是沒有 return {...} 區塊。這樣的語法很容易讀,不過也有另一種寫法,如下,我建議用第一種就好,比較簡單易懂,而且少打幾個字。

  methods: {
    onChange: function() {
      this.total = this.myprice * this.myamount
    }
  }

執行結果如下:

這個範例中,我們同時做到兩件事情,一是網頁資料與程式資料綁定,二是當綁定資料改變,引發事件去改變其他資料。

<input> 的資料型態

在此,我們擴大討論一下 <input> 內建的屬性,當我們簡單定義 <input> 的時候,內定的資料型態是文字,出現的介面也就只是一個間單的方框。

但是,如果我們很明確知道要輸入的值是數字時,就可以在 <input> 裡面加入可以控制資料型態的屬性 type="number" ,執行結果如下:

從執行結果來看,當設定 type="number" 之後,出現的就不只是方框了,而是一個含有捲動按鈕的數字框。以後就不用辛苦的輸入數字,只要用滑鼠點擊即可。

前面所使用的元件,其實是 HTML 的元件,再加上 v-model 跟 @change,頂多就是用到了 Vue 特色的元件,跟 Quasar 好像沒有特別的關係。這樣似乎有點掛羊頭賣狗肉,不是我的作風。

接下來,我們就來用一下真正的 Quasar 表單元件吧。

Quasar 表單元件

Quasar 處理表單的元件,打包在 QField 中,而被包在 Field 裡面的元件,包含 QInput, QSelect, QDatetime, 以及 QChipsInput 。

在第一次使用表單元件時,要在 /quasar.conf.js 導入,如下圖,先找到 framework:  區塊,將 components: 前面的註釋符號刪除,再填入 'QField' 即可。

    framework: {
      config: {},

      // iconSet: 'material-icons', // Quasar icon set
      // lang: 'en-US', // Quasar language pack

      // For special cases outside of where the auto-import strategy can have an impact
      // (like functional components as one of the examples),
      // you can manually specify Quasar components/directives to be available everywhere:
      //
      components: ['QField'],
      // directives: [],

      // Quasar plugins
      plugins: []
    },

同樣手法,新增一個 QuasarPrice.vue 元件,將原本的 <input> 語法全部改成 <q-input> ,只要是 Quasar 的元件,一律都是 q- 開頭,因此也特別提醒,自己建立的元件的命名方式要注意,不要用 q- 來命名,以免跟系統混淆。

記得,<q-input> 要用 <q-field> 包裹起來。

<template>
  <q-field label = "漢堡單價">
    <q-input v-model="myprice" @change="onChange"/>
  </q-field>
  <q-field label = "購買數量">
    <q-input v-model="myamount" @change="onChange"/>
  </q-field>
  總經額 {{total}} 元。
</template>

程式部分完全不變,連 @change 事件都可以共用,比想像中的簡單很多。

同樣的,我們在 About.vue 裡面使用:

<template>
  <q-page class="flex flex-center">
    ...
    <quasar-price/>
  </q-page>
</template>

<script>
import QuasarPrice from 'src/components/QuasarPrice.vue';
...
export default defineComponent({
  components: { SayHello, SayHello2, TotalPrice, TotalPrice2, QuasarPrice },
  name: 'PageAbout'
})
</script>

執行結果如下圖:


至此,我們就正式進入了 Quasar 元件的實作,也體會到 MVVM 的實際執行狀況。後面的單元˙就屬於進階部分,將深入介紹 QSelect 元件。


留言

這個網誌中的熱門文章

Python可以這樣玩(16):共陰/共陽七段顯示器

Python可以這樣玩(11):數學繪圖

Python可以這樣玩(15):蜂鳴器與音樂