Quasar 學習筆記 Ep 05 - Quasar 自製標籤的 Props 輸入與 Data() 輸出

 Quasar 自製標籤的 Props 輸入與 Data() 輸出

前一篇分享最後面有提及,使用 HTML 標籤時,通常會在標籤中加入一些屬性,例如:

  <q-layout view="lHh Lpr lFf">
  <q-header elevated>
  <q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer"/>

我們所設計的 <say-hello/> 標籤,只會固定輸出 Hello World!,如果我們希望,讓 World 變成一個可變動的字串,當我們單純使用<say-hello/> 時,就輸出 Hello World!,但是如果我們加入的屬性 <say-hello msg = "Quasar"/> 。就會輸出 Hello Quasar!。

從這篇分享中,你可以學到:

  1. 標籤屬性設計
  2. 從標籤傳入屬性值
  3. JavaScript 對文字物件的處理
  4. props 與 data() 的比較
  5. data() 的語法

從 SayHello 複製 SayHello2 元件

為了不影響 SayHello 元件,請在 components 目錄新增一個 SayHello2.vue,並 Copy SayHello 元件的內容。

就操作的方便性來說,可以在 SayHello.vue 上面按滑鼠右鍵選複製,然後在 Components 目錄上面按滑鼠右鍵選貼上。然後重新命名,開啟 SayHello2.vue 後,針對內容做一些小修改以免跟 SayHello 完全相同,修改後如下:

<template>
  <h4>Hello World!</h4>
</template>

<script>
export default {
  name: 'SayHello2'
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h4 {
  margin: 24px 0 0;
  color: green;
}
</style>

物件導向程式設計

如果你是接受過正規訓練的程式設計師,一定學過 C++ 或是 Java,因此對 "物件導向程式設計" 一定不陌生。但是請注意,我的重點並不在於 Vue.js 中 extends 或 mixins 的 OOP 相關的繼承語法,而只是單純的以物件的角度,來理解<script>中程式的內容。

當我們使用 C++ 或 Java 設計一個物件的時候,物件裡面會有兩個基本要素,一個是屬性、另一個是方法。而Vue <Script> 裡面定義的物件,也包含了屬性與方法。

我們先談屬性,因為這裡要談的就是 HTML 裡面屬性的設計方法。請在 SayHello2.vue 裡面加上 Props 程式碼:

<script>
export default {
  name: 'SayHello2',
  props: {
    msg: String
  }
}
</script>

在這裡,我們定義了一個名為 SayHello2 的 "物件" ,在這個 "物件" 裡面,我們定義了一個屬性 (Props) msg,而 Props 的主要目的,是來接收標籤中的屬性值。msg 的資料型態為 String。

當我們在 <script> 中定義了 msg 屬性之後,就可以在 <template> 中以 {{...}} 來使用它,如下:

<template>
  <h4>Hello {{msg}}!</h4>
</template>

在標籤中傳入屬性值

接下來,我們在 About.vue 裡面使用 SayHello2,我們一樣使用 IntelliSense 的方式來撰寫程式:


在 </p> 的下一行,輸入 <say... ,選取 say-hello-2,並輸入 msg 的傳入值,如下:

    <say-hello/>
    <p>
      中央流行疫情指揮中心今(21)日表示,本(2021)年12月14日起入境擇定春節檢疫專案C方案之民眾,
      將自12月21日起陸續期滿168小時,凡經檢疫第6天PCR檢測陰性及返家居家檢疫環境與條件均符合規定者,
      請全程佩戴口罩,搭乘地方政府安排之防疫車輛,返家接續後7天在家居家檢疫,並配合以下檢測措施
    </p>
    <say-hello-2 msg="Quasar"/>

由於使用的 IntelliSense,以下的程式碼會自動完成:

<script>
import SayHello from 'src/components/SayHello.vue';
import SayHello2 from 'src/components/SayHello2.vue';
import { defineComponent } from 'vue';

export default defineComponent({
  components: { SayHello, SayHello2 },
  name: 'PageAbout'
})
</script>

執行結果如下:


屬性的預設值與其它處理

前面的例子中,當我們以 <say-hello-2 msg="Quasar"/> 引用時,msg 會將 Quasar 字串傳入,但是實際上很多標籤的屬性都是可有可無的,如果我們也允許 <say-hello-2/> 語法,我們就必須將 msg 設定一個預設值,以增加使用的彈性:

<script>
export default {
  name: 'SayHello2',
  props: {
    msg: {
      type: String,
      default: 'World'
    }
  }
}
</script>

這時,如果我們以  <say-hello-2/> 引用,就會輸出 default 值 World。

<template>
  <q-page class="flex flex-center">
    <h3>關於這個網站</h3>
    <say-hello/>
    <p>
      中央流行疫情指揮中心今(21)日表示,本(2021)年12月14日起入境擇定春節檢疫專案C方案之民眾,
      將自12月21日起陸續期滿168小時,凡經檢疫第6天PCR檢測陰性及返家居家檢疫環境與條件均符合規定者,
      請全程佩戴口罩,搭乘地方政府安排之防疫車輛,返家接續後7天在家居家檢疫,並配合以下檢測措施
    </p>
    <say-hello-2 msg="Quasar"/>
    <say-hello-2/>  
  </q-page>
</template>


由於 JavaScript 本身是物件導向語言,所以我們可以使用 "object.method()" 的語法來做一些額外的處理。msg 的型態是 String,因此,我們可以使用 String 物件所提供的方法。例如,如果我們希望 msg 在輸出的時候,可以將字串轉換成大寫,我們可以這樣寫:

<template>
  <h4>Hello {{msg.toUpperCase()}}!</h4>
</template>


如果我們希望將 msg 字串反轉,則稍微複雜一些,需要用三個方法來達成:

<template>
  <h4>Hello {{msg.split('').reverse().join('')}}!</h4>
</template>

執行的順序是,split('') 將字串轉換成 array,reverse() 再將 array 反轉,最後再 join('') 成新的字串:


一個特殊的 Method -- data()

在繼續我們的程式之前,我先針對程式的顯示做一些整理,讓輸出效果好一點。請打開 About.vue ,我們針對 <style> 部分做一些修改,就是把這個頁面中會用到的 HTML 標籤的式樣做簡單的定義:

<style scoped>
h3 {
  margin: 5px;
  color: blue;
}
p {
  margin: 5px;
  color: purple;
}
</style>

在這裡重新定義了 Margin,讓每個顯示區塊有一致的留白寬度。至於 Margin 所代表的意義,可以由下面的圖形大致了解。如果有興趣,可以自行試試。


About.vue 裡面定義了 h3的式樣,再進入 SayHello.vue 裡面定義 h4,完整程式如下:

<template>
  <h4>Hello World!</h4>
</template>

<script>
export default {
  name: 'SayHello'
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h4 {
  margin: 5px;
  color: orange;
}
</style>

再進入 SayHello2.vue 定義 h5,完整程式如下:

<template>
  <h5>Hello {{msg.toUpperCase()}}!</h5>
</template>

<script>
export default {
  name: 'SayHello2',
  props: {
    msg: {
      type: String,
      default: 'World'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h5 {
  margin: 5px;
  color: green;
}
</style>

最後,我把 About.vue 的 <p> 修改成說明,這樣比較清楚我們做了那些測試:

<template>
  <q-page class="flex flex-center">
    <h3>關於這個網站</h3>
    <say-hello/>
    <p>上方顯示了網頁標題以及 say-hello 元件的固定標題文字。其中網頁標題的樣式為 h3,
       而 say-hello 標題樣式為 h4。</p>
    <say-hello-2 msg="Quasar"/>
    <say-hello-2/>  
    <p>上方顯示了 say-hello-2 元件的動態標題文字,會以 msg 屬性傳入要打招呼的對象字串,
       如果沒有傳入屬性,預設值為 World!</p>
  </q-page>
</template>

如果你有試著把瀏覽器放大所小,就會看到 RWD 的效果,裡面不論是 heading 或是文字段落,都是向右延伸而且居中顯示,這是因為 <q-page class="flex flex-center"> 的緣故。

言歸正傳,前面的範例都只使用到 props,這個屬性的用途在於接收標籤傳入的值,這個值會直接在標籤內容中以 {{prop_name}} 的方式顯示其值。一般來說,props的值是由外部傳入的,我們不會在程式裡面修改其值。但是如果我們要顯示的值,是必須由程式運算而產生的呢?這時我們就可以用 data() 來達成。

例如,我們想展示 漢堡單價 50 元,買 12 個,共 600 元。  這段文字,其中 price = 50、amount = 12,total = price * amount 則是個運算式。price / amount 是傳入值,我們可以定義為 props,而 total 為運算值,我們就必須定義為 data()。

建立一個 TotalPrice.vue 元件,內容如下:

<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>

Price 跟 Amount 沒有問題,我們將他們定義成 props,型態為 Number,並給予預設值。重點在於 data(),標題處已經闡明,data() 可以視為一個特殊的 Method,因為是 Method,所以我們要用 Method 的語法:data() {...} 來定義它,裡面只有一個指令,就是 return {...} ,代表它只做一件事情,就是把值傳回來。

data() 的基本語法如下:

  data() {
    return {
      data_name: 100
    }
  }

data_name 是一個變數名稱,冒號的後面則直接給予一個值,但是在這裡,我們不是單純給予一個數值,而是 price * amount,又由於 props 跟 data() 在同一層,如果在 data() 中要引用 props 中的值,必須要用 this.price * this.amount 才行。最後得到我們的程式碼:

  data() {
    return {
      total: this.price * this.amount
    }
  }

在 <template> 中要引用變數,data() 與 props 是一樣的:

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

最後,在 About.vue 裡面加入三行:

<template>
  <q-page class="flex flex-center">
    ...
    <total-price price = "50" amount = "12"/> ...(1) 整行
  </q-page>
</template>

<script>
...
import TotalPrice from 'src/components/TotalPrice.vue'; ...(2) 整行
export default defineComponent({
  components: { SayHello, SayHello2, TotalPrice }, ...(3) TotalPrice 部分
  ...
})
</script>

執行結果如下:



下一篇分享,將進入最精采的部分,就是 MVVM 的資料綁定。


留言

這個網誌中的熱門文章

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

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

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