keyboard_tabSkip to content
listVue Firebase Masterclass

ทำหน้าจัดการ Profile

สารบัญ

ย้ายฝั่งมาทำหน้า Profile บ้างก่อนจะไปหน้าตะกร้าสินค้า (จะได้เสร็จไปส่วนๆไป)

ทำ ProfileView

สิ่งที่เราจะทำ

  • ทำหน้า Profile ที่สามารถกรอก name, email และ profileImage โดย profileImage สามารถเปลี่ยนได้
  • สามารถทำการ save profile ทั้งหมด เอาไว้ได้ใน localstorage และสามารถถึงข้อมูลกลับมาได้ตอน refresh หน้าเว็บ
html
<script setup>
import { ref, reactive, onMounted } from 'vue'
import UserLayout from '@/layouts/UserLayout.vue'

const userForm = [
  { name: 'Email', field: 'email' },
  { name: 'Name', field: 'name' }
]

const userData = reactive({
  imageUrl: 'https://mikelopster.dev/mikelopster.da6b9a03.webp',
  email: '',
  name: ''
})

onMounted(() => {
  const savedUserProfile = localStorage.getItem('user-profile')
  if (savedUserProfile) {
    const userProfile = JSON.parse(savedUserProfile)
    userData.imageUrl = userProfile.imageUrl
    userData.email = userProfile.email
    userData.name = userProfile.name
  }
})

const handleFileChange = (event) => {
  const file = event.target.files[0]

  if (file) {
    const reader = new FileReader()
    reader.onload = (e) => {
      userData.imageUrl = e.target.result
    }
    reader.readAsDataURL(file)
  }
}

const updateProfile = () => {
  localStorage.setItem('user-profile', JSON.stringify(userData))
}
</script>

<template>
  <UserLayout>
    <div
      class="container mx-auto max-w-2xl p-4 bg-base-100 my-4 border border-base-200 shadow-md"
    >
      <h1 class="text-2xl">Your profile</h1>
      <div class="flex flex-col items-center">
        <div class="avatar">
          <div class="w-24 rounded-full">
            <img :src="userData.imageUrl" />
          </div>
        </div>

        <input type="file" @change="handleFileChange">

        <div v-for="item in userForm" class="form-control w-full">
          <label class="label">
            <span class="label-text">{{ item.name }}</span>
            <span class="label-text-alt"></span>
          </label>
          <input
            type="text"
            placeholder="Type here"
            class="input input-bordered w-full"
            v-model="userData[item.field]"
          />
        </div>

        <button class="btn btn-primary w-full mt-4" @click="updateProfile">Update profile</button>
      </div>
    </div>
  </UserLayout>
</template>

ผลลัพธ์ profile-02

เพิ่ม Event success ผ่าน store event.js

  • เพิ่ม Event store สำหรับ update popup message และ state สำหรับการแสดง Alert
  • เราจะใช้ toast ของ DaisyUI ในการแสดงผลออกมาว่า update ข้อมูลเรียบร้อย = สร้าง component Toast.vue ขึ้นมา
  • เรียกใช้งานจาก App.vue โดยใช้ store event.js และ Toast.vue สำหรับแสดงผล

เพิ่ม event.js

javascript
import { defineStore } from 'pinia'

export const useEventStore = defineStore('event', {
  state: () => ({
    alert: false,
    data: {}
  }),
  actions: {
    popupMessage (status, message) {
      this.data = {
        status,
        message
      }
      this.alert = true
      setTimeout(() => {
        this.clearPopup()
      }, 3000)
    },
    clearPopup () {
      this.alert = false
      this.data = {}
    }
  }
})

เพิ่ม components/Toast.vue

html
<script setup>
import { defineProps } from 'vue'

defineProps({
  message: String,
  status: String
})
</script>

<template>
  <div class="toast z-10">
    <div :class="`alert alert-${status}`">
      <span>{{ message }} {{ status }}</span>
    </div>
  </div>
</template>

เพิ่ม Toast ใน App.vue

html
<script setup>
// เพิ่ม Event store
import { useEventStore } from '@/stores/event'
const eventStore = useEventStore()

// import Toast component
import Toast from '@/components/Toast.vue'
</script>

<template>
  <div>
    <Toast
      v-if="eventStore.alert"
      :status="eventStore.data.status"
      :message="eventStore.data.message"
    >
    </Toast>
    <RouterView />
  </div>
</template>

ผลลัพธ์ profile-01

เพิ่ม Action ที่เข้าได้จาก Navbar UserLayout

  • เพิ่ม link ที่เป็นทางเข้าสำหรับหน้า profile โดยการเพิ่ม RouterLink เข้าไป

ที่ UserLayout

html
<script setup>
// เพิ่ม RouterLink เข้ามา
import { RouterLink, useRouter } from 'vue-router'
</script>

<template>
      <div class="flex-none">
        <div v-if="!isLoggedIn" class="btn btn-ghost" @click="login">
          Login
        </div>
        <div v-else class="dropdown dropdown-end">
          <label tabindex="0" class="btn btn-ghost btn-circle avatar">
            <div class="w-10 rounded-full">
              <img src="https://mikelopster.dev/mikelopster.da6b9a03.webp" />
            </div>
          </label>
          <ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
            <li>
              <!-- เพิ่ม link มา -->
              <RouterLink to="/profile" class="justify-between">
                Profile
              </RouterLink>
            </li>
            <li>
              <a @click="logout">Logout</a>
            </li>
          </ul>
        </div>
      </div>
</template>

ผลลัพธ์ profile-03