Skip to main content

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 ทั้งหมดจาก API
    • loadTodo (id) สำหรับดึง todo เฉพาะ id ออกมา
    • addTodo (todoText) สำหรับเพิ่ม todo เข้าไป
    • editTodo (todoData, id) สำหรับแก้ไข todo โดยระบุตาม id
    • removeTodo (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>

ผลลัพธ์ทั้งหมด workshop-01

และนี่ก็คือตัวอย่างการต่อ API เข้ากับ pinia เราจะเก็บ project นี้ไว้เพื่อใช้สำหรับ แต่งใน Session ต่อไป

โดยเราจะนำ Todo list นี้มาแต่งเป็นหน้าตาประมาณนี้ออกมาใน Session ต่อไปกัน

workshop-01