การกำหนด Props และ Event ให้กับ Component ตอนที่ 2

บทความ เมื่อไม่กี่สัปดาห์ โดย Ninenik Narkdee
props event defineemits defineprops component vuejs

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ props event defineemits defineprops component vuejs

ดูแล้ว 179 ครั้ง


เนื้อหานี้ต่อจากตอนแรกเกี่ยวกับ component เบื้องต้น จะมาดูเกี่ยวกับ
การกำหนด property หรือ props ซึ่งในขั้นตอนการเรียกใช้งาน เรา
ต้องการให้คอมโพเนนท์รองรับค่าจากการส่งเข้ามาเพื่อใช้งาน เพื่อให้เห็นภาพ
จะยกตัวอย่างโค้ดจากตอนที่แล้วเข้ามาประกอบการอธิบาย
 

การกำหนด Props ให้กับ Component

ใน ButtonCounter คอมโพเนนท์ในตอนที่แล้ว เราทำการนับจำนวนการคลิก
ซึ่งนับจาก 0 และเพิ่มทีละ 1 ค่า แต่สมมติว่า เราต้องการให้คอมโพเนนท์นี้ รองรับให้
สามารถกำหนดค่าเริ่มต้น และกำหนด step ของการเพิ่มค่าได้ เช่น แทนที่จะเป็น
บวกเพิ่มทีละ 1 ค่า ก็สามารถบวกตามที่ค่าที่กำหนดเข้ามาได้ เราสามารถทำได้ดังนี้
 
รูปแบบเดิม ButtonCounter.vue
 
<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <button @click="count++">
  You clicked me {{ count }} times.
  </button>
</template>
 
ปรับเป็นดังนี้ ButtonCounter.vue
 
<script setup>
import { ref } from 'vue'


const props = defineProps({
  start: {
    type: Number,
    required: true,
    default: 0,
    validator: value => value >= 0
  },
  step:{
    type: Number,
    default: 1,
    validator: value => value >= 1
  },
})


const count = ref(props.start)
function increment(){
  count.value +=props.step
}
</script>

<template>
  <button @click="increment">
  You clicked me {{ count }} times.
  </button>
</template>
 
defineProps เป็นคำสั่ง macro ใน vuejs ที่มีใช้ในส่วนของ <script setup> เพื่อทำให้การ
เขียนโค้ดง่ายขึ้นและกระชับขึ้น โดยการลดส่วนโค้ดที่ซ้ำซ้อน macro ที่เด่นชัดใน VueJS ได้แก่ 
defineProps, defineEmits, และ defineExpose. 
 

การใช้งาน defineProps กำหนดได้ทั้งแบบ array หรือ object 

// using Array syntax
const props = defineProps(['foo', 'bar'])
// using Object syntax
const props = defineProps({
  foo: String,
  bar: {
    type: Number,
    required: true
  }
})
 
หาก props ที่เรากำหนดเป็นข้อมูลแบบ String หรือข้อความ เราสามารถกำหนดในรูปแบบ
array ได้จะสะดวกกว่า แต่ถ้าเป็นตัวเลข หรือแบบที่ซับซ้อนกว่า หรือต้องการกำหนดเงื่อนไขเพิ่ม
เติม เช่น กำหนดชนิดข้อมูล มีค่าเริ่มต้น หรือกำหนดว่าต้อง ใช้งานหรือมี props นี้ทุกครั้งที่
เรียกใช้ หรือกำหนดการตรวจสอบข้อมูลว่าถูกต้องหรือไม่ เช่น เป็นค่ามากกว่าหรือเท่ากับ 0 
แบบนี้เราจะใช้เป็นการกำหนดแบบ object  ในตัวอย่าง เรากำหนดให้ทั้ง start และ step
เป็นตัวเลข และมีค่าเริ่มต้นเท่ากับ 0 และ 1 ตามลำดับ และเงื่อนไขการตรวจสอบ ต้องมากกว่า
หรือเท่ากับ 0 สำหรับ start และต้องมากกว่าหรือเท่า 1 สำหรับ step
เมื่อเรากำหนดเสร็จแล้ว ในขั้นตอนการเรียกใช้งาน เราก็จะปรับไฟล์โค้ดใน App.vue เป็นดังนี้
 
App.vue
<script setup>
import ButtonCounter from './components/ButtonCounter.vue'
</script>

<template>
  <h1>Here are many child components!</h1>
  <ButtonCounter :start="2" :step="3" />
  <ButtonCounter :start="0" />
</template>
 
จะเห็นว่าเมื่อเรากำหนดว่า start ต้องมีเสมอ ทำให้ตัวที่สอง ถึงค่า start จะเป็น 0 แต่เราก็จำเป็น
จะต้องเพิ่ม props เข้าไป ถึงแม้ว่าเราไม่เพิ่ม แต่โค้ดก็ยังทำงานปกติ จะมีแค่แจ้งเตือนใน console
ว่า [Vue warn]: Missing required prop: "start" ทั้งนี้ก็เพราะเรากำหนดค่าเริ่มต้นไว้ทั้ง
start และ step และไม่ได้กำหนดการตรวจสอบการจัดการไว้  สมมติว่าถ้าเรากำหนดการตรวจสอบ
การจัดการไว้เป็นดังนี้

ButtonCounter.vue
<script setup>
import { ref } from 'vue'


const props = defineProps({
  start: {
    type: Number,
    required: true,
    validator: value => value >= 0
  },
  step:{
    type: Number,
    default: 1,
    validator: value => value >= 1
  },
})

// ตรวจสอบว่า prop 'start' ถูกส่งมาหรือไม่
if (props.start === undefined) {
  throw new Error('Missing required prop: "start"');
}
const count = ref(props.start)
function increment(){
  count.value +=props.step
}
</script>

<template>
  <button @click="increment">
  You clicked me {{ count }} times.
  </button>
</template>
 
เรากำหนดให้ start จำเป็นต้องมี และไม่กำหนดค่าเริ่มต้น และให้มีการตรวจสอบว่ามีการส่งค่า
มาหรือไม่ ถ้าไม่มีการส่งค่ามาก็จะให้แจ้ง error ออกไป สมมติเราใช้งานเป็นดังนี้
 
App.vue
<script setup>
import ButtonCounter from './components/ButtonCounter.vue'
</script>

<template>
  <h1>Here are many child components!</h1>
  <ButtonCounter :start="2" :step="3" />
  <ButtonCounter  />
</template>
 
ผลลัพธ์ที่ได้
 


 
 
มี error เกิดขึ้นและ คอมโพเนนท์ตัวที่สองไม่ถูกนำมาแสดง เพราะไม่ได้กำหนด props ชื่อ 
start เข้าไปตามรูปแบบที่กำหนดไว้ 
 
จะสังเกตเห็นว่า required  และ validator ใน defineProps จะเป็นการกำหนดให้เราเห็น
ชัดว่ารูปแบบที่ต้องการเป็นอย่างเท่านั้น แต่ในเรื่องของการตรวจสอบ เราจำเป็นต้องสร้างเงื่อนไข
เพิ่มเติมเข้าไป หากต้องการตรวจสอบที่มากขึ้น เช่น
 
// ตรวจสอบว่า prop 'start' ถูกส่งมาหรือไม่และผ่านการ validate หรือไม่
if (props.start === undefined || !props.start >= 0) {
  throw new Error('Invalid prop: "start" must be a number greater than or equal to 0');
}
 
ดังนั้นเมื่อมีการนำไปใช้งานการกำหนดในรูปแบบ object เราต้องตรวจสอบให้ถูกต้อง 
 
อีกข้อสังเกตหนึ่งคือ โดยทั่วไป เวลากำหนด props ต่างๆ หรือ attribute ต่างๆ ใน html จะเป็น
การส่งข้อมูลเป็น string เข้าไป  ซึ่งถ้าเราใช้รูปแบบเป็น
 
<ButtonCounter start="2" step="3" />
 
จะกลายเป็นว่า เลข 2 และ 3 จะเป็นข้อมูล String ซึ่งไม่ตรงตามที่เรากำหนด ทำให้การทำงาน
อาจจะผิดพลาดได้ ด้วยเหตุนี้ กรณีที่ props ของเราไม่ใช่ข้อความหรือ String เราจึงใช้วิธีการ
เรียกใช้งาน v-bind directive ในรูปแบบ v-bind:start="" หรือ เขียนแบบย่อๆ เป็น :start=""
ซึ่งจะทำให้ vue ทำการตรวจสอบและแปลงข้อมูลเลข 2 กับ 3 ไปเป็นตัวเลข แทนที่จะเป็นข้อความ
เพื่อให้ตรงกับรูปแบบ props ที่เรากำหนดในคอมโพเนนท์ เราจึงนิยมใช้แบบนี้แทน
 
<ButtonCounter :start="2" :step="3" />
 

ดูเพิ่มอีกตัวอย่าง 

ให้เราสร้างคอมโพเนนท์ชื่อ BlogPost.vue ใน src > components ด้วยรูปแบบอย่างง่าย
และกำหนด props เข้าไปดังนี้
 
BlogPost.vue
<script setup>
defineProps(['title'])
</script>

<template>
  <h4>{{ title }}</h4>
</template>
 
และเรียกใช้งานในไฟล์ App.vue ดังนี้

App.vue
<script setup>
import ButtonCounter from './components/ButtonCounter.vue'
import BlogPost from './components/BlogPost.vue'
</script>

<template>
  <h1>Here are many child components!</h1>
  <ButtonCounter :start="2" :step="3" />
  <ButtonCounter  />

  <BlogPost title="My journey with Vue" />
  <BlogPost title="Blogging with Vue" />
  <BlogPost title="Why Vue is so fun" />  
</template>
 
ผลลัพธ์ที่ได้
 


 
 
สมมติเรามีข้อมูล และต้องการวนลูปแสดงด้วย v-for โดยใช้งานร่วมกับ BlogPost คอมโพเนนท์
ก็สามารถใช้งานได้ดังนี้

App.vue
<script setup>
import { ref } from 'vue'
import ButtonCounter from './components/ButtonCounter.vue'
import BlogPost from './components/BlogPost.vue'


const posts = ref([
  { id: 1, title: 'My journey with Vue' },
  { id: 2, title: 'Blogging with Vue' },
  { id: 3, title: 'Why Vue is so fun' }
])
</script>

<template>
  <h1>Here are many child components!</h1>
  <ButtonCounter :start="2" :step="3" />
  <ButtonCounter  />

  <BlogPost
  v-for="post in posts"
  :key="post.id"
  :title="post.title"
 />
</template>
 
ผลลัพธ์ที่ได้ก็จะเหมือนกับรูปตัวอย่างก่อนหน้าด้านบน

 
 

การกำหนด Event ให้กับ Component

ใน ButtonCounter เราเรียกใช้งาน onclick event เพื่อทำการเพิ่มจำนวนการคลิก แต่
เมื่อนำคอมโพเนนท์มาใช้ใน parent เราต้องการให้คอมโพเนนท์นั้นๆ มี event ของตัวเอง
เพื่อให้สามารถนำมาใช้งาน หรือสามารถเรียกใช้ event ของ คอมโพเนนท์ได้ เราต้องทำการ
กำหนด event ให้กับคอมโพเนนท์นั้น โดยสามารถทำได้ดังนี้

ButtonCounter.vue
 
<script setup>
import { ref } from 'vue'

const props = defineProps({
  start: {
    type: Number,
    default: 0
  },
  step:{
    type: Number,
    default: 1
  },
})

// กำหนด event ที่จะปล่อยไปกับคอมโพเนนท์
const emit = defineEmits(['increment','reset'])

const count = ref(props.start)
function increment(){
  count.value +=props.step
  // ปล่อย increment โดยส่งค่าข้อมูลออกไปด้วย
  emit('increment', count.value)
}
function resetstart(){
  count.value = props.start
  // ปล่อย reset โดยไม่ส่งค่าข้อมูลออกไป
  emit('reset') 
}
</script>

<template>
  <button @click="increment" @dblclick="resetstart" >
  You clicked me {{ count }} times.
  </button>
</template>
 
เริ่มต้นที่เรากำหนดชื่อ event ที่ต้องการ
 
// กำหนด event ที่จะปล่อยไปกับคอมโพเนนท์
const emit = defineEmits(['increment','reset'])
 
จากนั้นส่งออกค่าในคำสั่ง หรือเหตุการณ์ ที่ต้องการ
 
function increment(){
  // ปล่อย increment โดยส่งค่าข้อมูลออกไปด้วย
  emit('increment', count.value)
}
function resetstart(){
  // ปล่อย reset โดยไม่ส่งค่าข้อมูลออกไป
  emit('reset') 
}
 
สังเกตการทำงานใน template ของคอมโพเนนท์
 
<button @click="increment" @dblclick="resetstart" >
You clicked me {{ count }} times.
</button>
 
เรากำหนดว่า เมื่อคลิก ให้ทำคำสั่ง increment() คือเพิ่มจำนวน และเมื่อเพิ่มจำนวนแล้ว
ก็ให้ปล่อย event ที่ชื่อว่า  increment ออกไป พร้อมกับค่าข้อมูล count ณ ปัจจุบัน
ในขณะที่ เมื่อกดดับเบิลคลิก ให้ทำคำสั่ง resetstart() คือให้ค่าเริ่มต้นของ count กลับ
มาเป็นค่าเริ่มต้น ตามที่กำหนดตอนแรก และเมื่อรีเซ็ตค่าแล้ว ก็ให้ปล่อย event ที่ชื่อว่า
reset ออกไป โดยไม่มีการส่งค่าข้อมูลไปด้วย
 
สรุปการทำงานก็คือ คลิก 1 ครั้งเป็นการเพิ่มจำนวน กดดับเบิลคลิกหรือสองครั้งติดกัน จะเป็น
การรีเซ็ตค่าการนับใหม่เป็นค่าเริ่มต้น
 
เมื่อกำหนด event ให้กับคอมโพเนนท์แล้ว เราก็สามารถเรียกใช้งานในส่วนของ parent 
ได้เป็นดังนี้

App.vue
<script setup>
import { ref } from 'vue'
import ButtonCounter from './components/ButtonCounter.vue'
import BlogPost from './components/BlogPost.vue'


const posts = ref([
  { id: 1, title: 'My journey with Vue' },
  { id: 2, title: 'Blogging with Vue' },
  { id: 3, title: 'Why Vue is so fun' }
])
function countupdate(newValue){
  console.log('Increment '+ newValue)
}
function countreset(){
  console.log('Reset ')
}
</script>

<template>
  <h1>Here are many child components!</h1>
  <ButtonCounter :start="2" :step="3" />
  <ButtonCounter @reset="countreset" 
  @increment="countupdate" />

  <BlogPost
  v-for="post in posts"
  :key="post.id"
  :title="post.title"
 />
</template>
 
ดูในส่วนของการเรียกใช้งานที่ปุ่ม ButtonCounter
 
<ButtonCounter @reset="countreset" 
  @increment="countupdate" />
 
จะเห็นรูปแบบการใช้งาน event โดยใช้ v-on:increment กับ v-on:reset ซึ่งเขียนแบบ
ย่อได้เป็น :increment="" กับ :reset="" ในตัวอย่าง เมื่อเกิด event ขึ้น เรากำหนดให้
ไปทำคำสั่ง countupdate และ countreset โดย countupdate จะรองรับข้อมูลที่ส่ง
มาพร้อมกับ event ด้วย 
 
function countupdate(newValue){
  console.log('Increment '+ newValue)
}
function countreset(){
  console.log('Reset ')
}
 
จะเห็นว่ารูปแบบการใช้งาน event ที่เราสร้างขึ้น ก็จะสามารถใช้งานได้คล้ายกับ DOM element
ปกติที่เราคุ้ยเคย 
 
เนื้อหาเกี่ยวกับการกำหนด props และ event รวมถึงการใช้งาน props และ event ที่กำหนด
ใน component เมื่อนำมาใช้งานใน parent ก็จะขอจบประมาณนี้ เนื้อหาเกี่ยวกับคอมโพเนนท์
เบื้องต้นยังมีต่อ รอติดตามในตอนที่ 3


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ



ทบทวนบทความที่แล้ว









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ