Skip to main content

เพิ่มเติม Pinia ด้วย Getters

รู้จักกับ Getters

Document ต้นฉบับ https://pinia.vuejs.org/core-concepts/getters.html

ทีนี้เราอาจจะมีเคสที่เราอาจจะมีการห่อ logic ใน state เพิ่มเติมบางอย่าง เพื่อให้สามารถใช้งาน state ที่มีการผสม logic ได้ (ไอเดียจะมีเหมือนกับ computed)

Pinia เองก็มีสิ่งนี้เช่นกัน เราเรียกกันว่า getters ซึ่งจาก document ของ pinia นั้นบอกว่า มีค่าเทียบเท่ากับ computed ซึ่ง getters จะสามารถใส่ logic กับตัว state เพิ่มได้นั่นเอง

ตัวอย่าง getters จะหน้าตาประมาณนี้

  • สมมุติเรามี CountersStore อยู่ แล้วเราต้องการตัวแปรสักตัวที่ double ค่า count ออกไปได้
  • เราเลยสร้าง getters ชื่อ doubleCounter และคืนค่า count * 2 ออกไปได้
import { defineStore } from 'pinia'

export const useCountersStore = defineStore('counters', {
state: () => ({ // กำหนดข้อมูล state ที่จะเก็บใน store นี้
count: 0
}),
getters: {
doubleCount(state) {
return state.count * 2
}
},
actions: { // function ที่จะใช้ใน store นี้
increment() {
this.count++
}
}
})

เมื่อเรียกใช้ผ่าน Component ก็จะสามารถเรียกมายัง key นั้นได้ (เรียกเหมือน computed เลย)

<script setup>
import { RouterLink } from 'vue-router'
import { useCountersStore } from '../stores/counters'

const counters = useCountersStore()

</script>

<template>
<div>
<div>{{ counters.count }}</div>
<div>{{ counters.doubleCount }}</div>
<button @click="counters.increment()">Add count</button>
</div>
</template>

ตัวอย่างกับ Profile เพิ่มเติมอีกสักเคส

สมมุติ เราจะให้ Profile สามารถแสดงชื่อจริงออกมาได้ (ต่อ firstname, lastname) เราจะสร้าง

  • getters fullname ขึ้นมาโดยเป็นการต่อระหว่าง firstname ต่อกับ lastname เข้าไป
  • และเรียกใช้ผ่านตัวแปร fullname จากทั้ง 2 หน้า (HomeView.vue, Profile.vue) ได้

เริ่มต้นแก้ที่ stores/user.js เพิ่ม getters fullname เข้ามา

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
state: () => ({
firstname: '',
lastname: ''
}),
getters: { // เพิ่ม getter เข้ามา
fullname (state) {
return `${state.firstname} ${state.lastname}`
}
},
actions: {
updateProfile(userData) {
this.firstname = userData.firstname
this.lastname = userData.lastname
}
}
})

ไปที่ ProfileView.vue ลองหยิบ fullname มาแสดงผล

<script setup>
import { RouterLink } from 'vue-router'
import { useUserStore } from '../stores/user'
import { onMounted, ref, watch } from 'vue'

const user = useUserStore()
const firstname = ref('')
const lastname = ref('')
const isUpdated = ref(false)

const updateProfile = () => {
user.updateProfile({
firstname: firstname.value,
lastname: lastname.value
})
isUpdated.value = true
}

watch([firstname, lastname], () => {
isUpdated.value = false
})

onMounted(() => {
firstname.value = user.firstname
lastname.value = user.lastname
})
</script>

<template>
<div>
Profile page
<div>
<div>Firstname</div>
<input type="text" v-model="firstname">
</div>
<div>
<div>Lastname</div>
<input type="text" v-model="lastname">
</div>
<div> <!-- เพิ่มตรงตำแหน่งนี้เข้ามา -->
Current fullname in store: {{ user.fullname }}
</div>
<div v-if="isUpdated">
Profile update to store
</div>
<button @click="updateProfile()">Update profile</button>
<div>
<RouterLink to='/'>Go to home</RouterLink>
</div>
</div>
</template>

ไปที่ HomeView.vue ให้เรียกใช้ fullname เหมือนกัน

<script setup>
import { RouterLink } from 'vue-router'
import { useUserStore } from '../stores/user'

const user = useUserStore()

</script>

<template>
<div>
<div>
(From profile)
<div>Firstname: {{ user.firstname }}</div>
<div>Lastname: {{ user.lastname }}</div>
<div>Fullname: {{ user.fullname }}</div>
</div>
<div>
<RouterLink :to="{
name: 'profile',
params: { id: 1 }
}">Go to profile</RouterLink>
</div>
</div>
</template>

ผลลัพธ์ได้ออกมาเป็นแบบนี้

getter-01

และนี่ก็คือ core concept หลักของ pinia จริงๆมันก็จะมีเรื่องเพิ่มเติมอย่าง plugin หรือการเชื่อมกันกับ router ซึ่งเราจะเพิ่มเติมกันในโจทย์ของ API กันอีกที