Workshop ประจำหัวข้อนี้
เราจะทำอะไรกัน ?
เราจะทำหน้า Profile ของ User ขึ้นมาโดยท ี่ user จะสามารถแก้
- ชื่อจริง (Firstname)
- นามสกุล (Lastname)
- อีเมล (Email)
โดยเราจะเพิ่ม action มาว่า
- แรกสุด เราจะใส่ข้อมูลเริ่มต้นตอน mount component (onMounted)
- เสร็จแล้วหลังจากแก้เสร็จ ให้ขึ้นแสดงข้อความว่า "Profile updated!" (method, watch)
- ระหว่างกดแก้ไข ไม่อนุญาติให้ user สามารถกดแก้ไขได้ถ้าไม่ผ่าน validate ต่อไปนี้ (watch)
- firstname, lastname ต้องไม่มีตัวเลข
- email ต้องมี @ อยู่
- รวมถึงหากเรากดส่ง ถ้าข้อมูลยัง update ไม่สำเร็จ = ต้องกดไม่ได้ (เราจะจำลองผ่าน delay กัน)
- แสดงชื่อเต็ม ออกมา (computed)
ส่วน import component
เดี๋ยวเราเอาไปลองใน session Router อันต่อไปกัน
<script setup>
import { ref, computed, watch, onMounted, onUpdated } from 'vue'
const firstName = ref('')
const lastName = ref('')
const email = ref('')
const errors = ref({})
const isUpdated = ref(false)
const isLoading = ref(false)
const isValid = ref(false)
const validateName = (name) => {
const re = /\d/
return !re.test(name)
}
const validateEmail = (email) => {
return email.includes('@')
}
// ใช้ compute แสดง fullname
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// ใช้ watch ดักจับข้อมูลเพื่อเช็ค validate ตลอด
watch([firstName, lastName, email], () => {
isUpdated.value = false
// reset validate ใหม่
isValid.value = true
errors.value = {}
if (!validateName(firstName.value)) {
isValid.value = false
errors.value.firstName = 'Firstname should not contain numbers'
}
if (!validateName(lastName.value)) {
isValid.value = false
errors.value.lastName = 'Last name should not contain numbers'
}
if (!validateEmail(email.value)) {
isValid.value = false
errors.value.email = 'Email is not valid'
}
})
onMounted(() => {
// กำหนด default value
firstName.value = 'ทดสอบ'
lastName.value = 'นามสกุล'
email.value = '[email protected]'
})
const saveProfile = async () => {
isLoading.value = true
// จำลองการส่ง API
await (new Promise(resolve => setTimeout(resolve, 2000)))
isLoading.value = false
isUpdated.value = true
}
</script>
<template>
<div class="container">
<h1>User Profile</h1>
<div>Fullname: {{ fullName }}</div>
<div>Email: {{ email }}</div>
<div class="profile-form">
<h2>Edit Profile</h2>
<div>
<div>First Name</div>
<input v-model="firstName">
<p class="error" v-if="errors.firstName">{{ errors.firstName }}</p>
</div>
<div>
<div>Last Name</div>
<input v-model="lastName">
<p class="error" v-if="errors.lastName">{{ errors.lastName }}</p>
</div>
<div>
<div>Email</div>
<input v-model="email">
<p class="error" v-if="errors.email">{{ errors.email }}</p>
</div>
<div class="loading" v-if="isLoading">
Loading...
</div>
<!-- ดักไว้ว่า ถ้าไม่ valid หรือกำลัง loading ไม่ให้โหลด -->
<button :disabled="!isValid || isLoading" @click="saveProfile">Save</button>
</div>
<p v-if="isUpdated">Profile updated!</p>
</div>
</template>
<style scoped>
.container {
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
}
.profile-form {
width: 480px;
}
.profile-form button {
width: 100%;
margin-top: 20px;
}
.profile-form input {
width: 100%;
height: 20px;
}
.profile-form .error {
color: red;
}
.loading {
padding: 10px;
margin-top: 20px;
background-color: aliceblue;
}
</style>
ไปเพิ่ม style ตัวนี้เพื่อให้ layout แสดง 100% ได้ ที่ style.css
body #app-new {
width: 100%;
}