Skip to main content

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 = 'test@mail.com'
})

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%;
}