Let's code แต่ละส่วนกัน
เริ่มต้น Structure project
เราจะมาทำ Todo list (อันว่าเป็น Hello world ของโลก Frontend ในยุคนี้กัน) โดยเราจะวาง Structure กันตามนี้
├── src
├── App.vue --> root component
├── router
│ └── index.js --> ตำแหน่งรวม router (ตำแหน่งเดิม)
├── stores
│ └── todo.js --> store สำหรับเก็บ todo list ไว้
└── views
│ ├── EditView.vue --> หน้าสำหรับแก้ไข EditView.vue
│ └── HomeView.vue --> หน้าสำหรับหน้าหลัก
└── main.js
เริ่มทำ todo store
ที่ stores/todo.js
- เพิ่ม state สำหรับเก็บ todo, selectedTodo และ status ทั้งหมดของ Todo เอาไว้
- เพิ่ม function
loadTodos()
สำหรับดึง Todo ทั้งหมดจาก APIloadTodo (id)
สำหรับดึง todo เฉพาะ id ออกมาaddTodo (todoText)
สำหรับเพิ่ม todo เข้าไปeditTodo (todoData, id)
สำหรับแก้ไข todo โดยระบุตาม idremoveTodo (id)
สำหรับลบ Todo
import { defineStore } from 'pinia'
import axios from 'axios'
const BASE_URL = 'https://64f192630e1e60602d23f251.mockapi.io'
export const useTodoStore = defineStore('todo', {
state: () => ({ // กำหนดข้อมูล state ที่จะเก็บใน store นี้
list: [],
selectedTodo: {},
statuses: ['Pending', 'Doing', 'Done']
}),
actions: {
async loadTodos () {
try {
const response = await axios.get(`${BASE_URL}/todos`)
this.list = response.data
} catch (error) {
console.log('error', error)
}
},
async loadTodo (id) {
try {
const response = await axios.get(`${BASE_URL}/todos/${id}`)
this.selectedTodo = response.data
} catch (error) {
console.log('error', error)
}
},
async addTodo (todoText) {
const bodyData = {
name: todoText,
status: 'Pending'
}
try {
const response = await axios.post(`${BASE_URL}/todos`, bodyData)
console.log(response.data)
// this.list = response.data
} catch (error) {
console.log('error', error)
}
},
async editTodo (todoData, id) {
try {
const bodyData = {
name: todoData.name,
status: todoData.status
}
const response = await axios.put(`${BASE_URL}/todos/${id}`, bodyData)
console.log(response.data)
} catch (error) {
console.log('error', error)
}
},
async removeTodo (id) {
try {
const response = await axios.delete(`${BASE_URL}/todos/${id}`)
console.log(response.data)
} catch (error) {
console.log('error', error)
}
}
}
})
ทำ Router และ View เริ่มต้น
ที่ router/index.js
- เพิ่ม 2 path มา คือ
/
สำหรับ HomeView/todo/:id/edit
สำหรับ EditView
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import EditView from '../views/EditView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/todo/:id/edit',
name: 'edit-view',
component: EditView
}
]
})
export default router
ทำการ Mock View ก่อน
- ที่
views/HomeView.vue
<template>
Todo list Home View
</template>
- ที่
views/EditView.vue
<template>
Todo list Edit View
</template>
ประก อบ HomeView.vue และ EditView.vue
ไฟล์ HomeView.vue
- เพิ่มตัวรับ Store เข้ามาแสดงผลที่ template
- เพิ่ม Todo list โดยการแสดง name, status และ Action (Edit, Delete) ให้สามารถไปยังหน้า Edit และลบได้
- เพิ่ม จังหวะโหลด API todo list ตอนเปิดหน้าขึ้นมา
- เพิ่ม Todo list จากหน้า Home ได้ (เป็น input ด้านบน)
- ปรับ status todo list ได้จากหน้า Home
- เพิ่ม Edit, Delete method เข้าไปและเพิ่ม Loading ให้รู้ว่ากำลังดำเนินการอยู่ (แบบง่ายๆ)
<script setup>
import { onMounted, ref } from 'vue'
import { RouterLink } from 'vue-router'
import { useTodoStore } from '../stores/todo'
const todoStore = useTodoStore()
const todoText = ref('')
const isLoading = ref(false)
onMounted(async () => {
await todoStore.loadTodos()
// console.log(todoStore.list) มันเป็น proxy = เราก็แก้ผ่านตัวนี้ก็ได้
})
const addTodo = async (todoText) => {
isLoading.value = true
try {
await todoStore.addTodo(todoText)
await todoStore.loadTodos()
} catch (error) {
console.log('error', error)
}
isLoading.value = false
}
const updateStatus = async (todoId, todoStatus) => {
isLoading.value = true
try {
await todoStore.editTodo({
status: todoStatus
}, todoId)
} catch (error) {
console.log('error', error)
}
isLoading.value = false
}
const removeTodo = async (id) => {
isLoading.value = true
try {
await todoStore.removeTodo(id)
await todoStore.loadTodos()
} catch (error) {
console.log('error', error)
}
isLoading.value = false
}
</script>
<template>
<div>
<input type="text" v-model="todoText">
<button @click="addTodo(todoText)">Add</button>
</div>
<div>
<div v-if="isLoading">
<h1>Loading...</h1>
</div>
<div>
<div class="flex" v-for="todo in todoStore.list" :key="todo.id">
<div>Item {{ todo.name }}</div>
<div>
Status
<select v-model="todo.status" @change="updateStatus(todo.id, todo.status)">
<option
v-for="status in todoStore.statuses"
:key="status"
:value="status"
>
{{ status }}
</option>
</select>
</div>
<div>
<RouterLink :to="{ name: 'edit-view', params: { id: todo.id } }">
<button>Edit</button>
</RouterLink>
<button @click="removeTodo(todo.id)">Delete</button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.flex {
display: flex;
}
.flex > div {
margin-right: 20px;
}
</style>
ไฟล์ EditView.vue
- ดึงข้อมูล id จาก param มา และยิงไปยัง Todo list เพื่อนำข้อมูลมาสำหรับแก้ไข (โดยใช้จาก Store)
- แ ก้ไข ชื่อและ status ได้ และสามารถยิงไปยัง Todo list API สำหรับแก้ไข todo list ได้
- มีตัว alert บอกมาตอน update เสร็จแล้ว
<script setup>
import { onMounted, ref, reactive } from 'vue'
import { useTodoStore } from '../stores/todo'
import { useRoute, RouterLink } from 'vue-router'
const todoStore = useTodoStore()
const route = useRoute()
const todoId = ref(-1)
const todoData = reactive({
name: '',
status: ''
})
const editTodo = async (todoData, todoId) => {
await todoStore.editTodo(todoData, todoId)
alert('update completed')
}
const isLoaded = ref(false)
onMounted(async () => {
try {
todoId.value = parseInt(route.params.id)
await todoStore.loadTodo(todoId.value)
todoData.name = todoStore.selectedTodo.name
todoData.status = todoStore.selectedTodo.status
isLoaded.value = true
} catch (error) {
console.log('error', error)
}
})
</script>
<template>
<div>
Edit View
<RouterLink :to="{ name: 'home' }">
Back
</RouterLink>
<div v-if="isLoaded">
<div>Name</div>
<input type="text" v-model="todoData.name">
<div>Status</div>
<select v-model="todoData.status">
<option
v-for="status in todoStore.statuses"
:key="status"
:value="status"
>
{{ status }}
</option>
</select>
<button @click="editTodo(todoData, todoId)">Update</button>
</div>
</div>
</template>
ผลลัพธ์ทั้งหมด
และนี่ก็คือตัวอย่างการต่อ API เข้ากับ pinia เราจะเก็บ project นี้ไว้เพื่อใช้สำหรับ แต่งใน Session ต่อไป
โดยเราจะนำ Todo list นี้มาแต่งเป็นหน้าตาประมาณนี้ออกมาใน Session ต่อไปกัน