keyboard_tabSkip to content
listVue Firebase Masterclass

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 อันต่อไปกัน

html
<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

css
body #app-new {
  width: 100%;
}