เกริ่นชื่อหัวข้อมาก็ปวดหัวแล้วเรามาเริ่มกันเลยดีกว่า ในการใช้งาน Freamwork ที่เป็น Base on Component นั้นเราต้องแยกไฟล์เป็นส่วนประกอบย่อยๆแล้วเอามารวมกันเป็นหน้า 1 หน้าที่นี้เวลาเราแยก component ออกไปเราก็ทำการส่งค่า props ไปหา component ลูกแบบนี้
<child-component :name="name"></child-component>
ง่ายใช่มั้ยละ (วิธีรับค่า props แบบปกติคงไม่ขอพูดถึงในบทความนี้เดี๋ยวจะยาว) แต่ถ้าเกิดใน child component มันดันเป็น input field, textarea, etc. ที่มันกรอกข้อมูลได้เราจะส่งมันกลับมายังไง ติ๊กต๊อกๆ..เห้ยทำยังไงนั่นคือคำถามแรกในหัวผมเลยเพราะปกติเราก็ส่ง props ไปหาตลอดไม่เคยให้ child คืนข้อมูลกลับมาเลยนอกจากการ emit เมื่อกดปุ่มแต่นี่เราต้องการให้พิมพ์แล้วข้อความมันส่งกลับมาเลยโดยไม่ต้องกดปุ่มอะไร
เอาล่ะมาดูวิธีการกันในตัวอย่างผมขอแบ่ง Component เป็น 3 ชั้นดังนี้
<template>
<div>
<h1>Todo List</h1>
<todo-list></todo-list>
</div>
</template>
<script>
import TodoList from './TodoList'
export default {
components: {
TodoList
}
}
</script>
ไฟล์นี้ทำการ import ตัว component todo-list เข้ามาปกติหลายคนอาจจะงงว่ามันมีทำไมในเมื่อเอาไปรวมกับ TodoList.vue ได้ อย่าลืมนะครับว่าเราควรแบ่ง component ให้เล็กที่สุดและอีกอย่างตัว layout นี้แหละที่ใช้เป็นตัว call API (แต่บทความนี้ไม่ขอพูดถึงจ้า) และส่งลงไปให้ child-component
<template>
<div class="container">
<div class="field-warp">
<field-list v-model="description"></field-list>
</div>
<pre>{{ description }}</pre>
</div>
</template>
<script>
import FieldList from './FieldList'
export default {
components: {
FieldList
},
data() {
return {
description: 'Hello'
}
}
}
</script>
<style>
.container {
width: 960px;
margin: 0 auto;
text-align: center;
}
.field-warp {
display: block;
}
</style>
ในไฟล์ TodoList.vue เราทำการเรียก component FieldList อีกต่อเพื่อจะนำ input ออกมา และ ทำการ print ค่าตัวแปร description ใน pre เพื่อดูว่า component FieldList จะสามารถส่งค่ากลับมาได้หรือไม่
<template>
<input type="text" :value="value" placeholder="Description" @input="updateValue($event.target.value)">
</template>
<script>
export default {
props: ['value'],
methods: {
updateValue(value) {
this.$emit('input', value)
}
}
}
</script>
component นี้เป็น input ธรรมดาเราจะมาดูกันว่าทำอย่างไรเมื่อเราต้องการให้ส่ง props ไปหา field-list และ ส่งกลับมาด้วยตัวแปรเดียวกัน
สังเกตุที่ไฟล์ TodoList.vue มีการส่ง v-model เข้าไปใน component
<field-list v-model="description"></field-list>
และในส่วนของ FieldList.vue จะไม่ได้รับ props แบบปกติแล้วเพราะเราไม่ได้ส่งอะไรเข้าไปแต่เราใช้ v-model ส่งทั้งตัวแปรและรับค่าเลยเราจึงต้องรับ props ชื่อ “value” เท่านั้นตาม Doc เลย
props: ['value'] // รับค่ามาจาก v-model="description"
และนำค่า value ไป bind ใส่ attribute value ของ input จากนั้นเมื่อเกิด event input ขึ้นให้ส่ง $event.target.value (ค่า value จากตัว input) ไปที่ฟังก์ชั่นแล้วทำการ $emmit ไปที่ event “input” มันจะส่งค่ากลับไปเข้าตัว v-modal=”description” อัตโนมัติเลย
<input type="text" :value="value" placeholder="Description" @input="updateValue($event.target.value)">
<script>
export default {
props: ['value'],
methods: {
updateValue(value) {
this.$emit('input', value)
}
}
}
</script>
จะได้หน้าตาแบบนี้
แต่เรื่องนี้มันไม่จบง่ายแบบนี้แน่ๆเพราะถ้ามันง่ายขนาดนี้จะมาเขียนบทความให้เมื่อยตัวทำไมเพราะในเมื่อ component FieldList ผมต้องการให้มี input มากกว่า 1 !!……
มาดู Requirement กันก่อน
- ต้องมีปุ่มกด ADD เมื่อกดแล้วจะเพิ่ม FieldList ขึ้นมา
- ใน FieldList นั้นจะมี 2 input หรือ มากกว่า
ในตอนแรกผมคิดหัวแทบแตกจะเขียนอย่างไรโดยใช้แค่ vue.js ไม่อยากไปใช้ jquery หรือ pure javascript เพื่อ access DOM เพราะไหนๆ vue.js มันก็ก็อบของดีอื่นๆมาตั้งเยอะมันต้องทำอะไรได้บ้างแหละจนพบ DOC เรื่อง Sync อ่านลงมาเรื่อยๆก็ได้ concept ตามด้านบนที่ได้เล่าไปแต่ทำไมมันไม่มี emit event ที่พ่น value ออกมาพร้อมกันเยอะๆพยายามหาบทความว่ามี v-model แบบ multiple value บ้างมั้ยจนหมดหนทาง ก็เลยไปถามกลุ่ม Vue.js Thailand ก็ได้แนวคิดและเอามารวมกับที่นั่งอ่านก็ได้ปิ๊งไอเดียในการทำ…….แต่ตอนนี้บทความมันยาวไปซะแล้วเดี๋ยวผมจะขอต่อใน path 2 นะครับ