102. Rendering Multiple Error Messages

npm create vue@latest


  1. ๋‹ค์ค‘ ์˜ค๋ฅ˜ ์ถœ๋ ฅ ์ตœ์ ํ™”

    • ์ง€๊ธˆ๊นŒ์ง€๋Š” ํ•œ ๊ฐœ์˜ ์˜ค๋ฅ˜๋งŒ ์ถœ๋ ฅํ–ˆ์ง€๋งŒ, ํŠน์ • ์ž…๋ ฅ ํ•„๋“œ์—์„œ ์—ฌ๋Ÿฌ ์˜ค๋ฅ˜๋ฅผ ์ถœ๋ ฅํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ์Œ
    • ์˜ˆ์‹œ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„๋“œ์—์„œ์˜ ๋‹ค์–‘ํ•œ ๊ทœ์น™ ์œ„๋ฐ˜์‹œ ์—ฌ๋Ÿฌ ์˜ค๋ฅ˜๋ฅผ ๋™์‹œ์— ์ถœ๋ ฅ
  2. ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„๋“œ์— ์ง‘์ค‘

    • ์ฒซ๋ฒˆ์งธ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„๋“œ์—๋งŒ ๊ทœ์น™ ์ ์šฉ
    • ๋‘๋ฒˆ์งธ ํ•„๋“œ๋Š” ์ฒซ๋ฒˆ์งธ ํ•„๋“œ์™€ ์ผ์น˜ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ณ„๋„ ๊ทœ์น™ ๋ถˆํ•„์š”
  3. ๊ฒ€์ฆ ์ „๋žต ๋ณ€๊ฒฝ

    • validate๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ fast exit ์ „๋žต์„ ์‚ฌ์šฉํ•ด ํ•˜๋‚˜์˜ ๊ทœ์น™์ด ์œ„๋ฐ˜๋˜๋ฉด ๋‚˜๋จธ์ง€ ๊ทœ์น™์€ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š์Œ
    • ์—ฌ๋Ÿฌ ์˜ค๋ฅ˜๋ฅผ ์ถœ๋ ฅํ•˜๊ธฐ ์œ„ํ•ด bails ์†์„ฑ์„ false๋กœ ์„ค์ •ํ•˜์—ฌ ๋ชจ๋“  ๊ทœ์น™์„ ๊ฒ€์‚ฌํ•˜๋„๋ก ๋ณ€๊ฒฝ

  1. ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์ปดํฌ๋„ŒํŠธ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•:

    • ๊ธฐ์กด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์ปดํฌ๋„ŒํŠธ๋Š” ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์˜ค๋ฅ˜๋งŒ ์ถœ๋ ฅ.
    • field ์ปดํฌ๋„ŒํŠธ์— ์Šฌ๋กฏ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ ๊ฐ€๋Šฅ.
  2. ์ž…๋ ฅ ํƒœ๊ทธ์™€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๊ตฌํ˜„:

    • field ํƒœ๊ทธ ๋‚ด์— input ํƒœ๊ทธ ์ถ”๊ฐ€ ๋ฐ ํ•„์š”ํ•œ ์†์„ฑ ์„ค์ •.
    • v-slot ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ field ์†์„ฑ ๊ตฌ์กฐ ๋ถ„ํ•ด.
    • field ์†์„ฑ์„ input ์š”์†Œ์— ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ๊ฒ€์ฆ ํ™œ์„ฑํ™”.
  3. ์˜ค๋ฅ˜ ๋ฐฐ์—ด ์ฒ˜๋ฆฌ:

    • errors ๋ฐฐ์—ด์„ ํ†ตํ•ด ์ž…๋ ฅ ํ•„๋“œ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜ ๋ชฉ๋ก ์ ‘๊ทผ.
    • v-for ์ง€์‹œ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณ„๋„์˜ ์ค„๋กœ ์ถœ๋ ฅ.
  4. ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ทœ์น™ ์—…๋ฐ์ดํŠธ:

    • ์ตœ์†Œ ๊ธ€์ž ์ˆ˜๋ฅผ 9๋กœ ์„ค์ •.
    • 'password'์™€ ๊ฐ™์€ ํŠน์ • ๋‹จ์–ด ์‚ฌ์šฉ ๊ธˆ์ง€ ๊ทœ์น™ ์ถ”๊ฐ€.
  5. ํ…Œ์ŠคํŠธ ๋ฐ ํ™•์ธ:

    • ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์ €์žฅ ํ›„ ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ.
    • ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„๋“œ์— 'password' ์ž…๋ ฅํ•˜์—ฌ ์—ฌ๋Ÿฌ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ ํ™•์ธ.
  6. ๋‹ค์Œ ๊ฐ•์˜ ๊ณ„ํš:

    • ํผ์˜ ์ถ”๊ฐ€ ์ตœ์ ํ™”๋ฅผ ๋‹ค์Œ ๊ฐ•์˜์—์„œ ๊ณ„์† ์ง„ํ–‰ ์˜ˆ์ •.

App.vue
<script setup lang="ts">
import useModal from '@/stores/modal'
import { storeToRefs } from 'pinia'
import { ref } from 'vue'

const modalStore = useModal()
const { hiddenClass, isOpen: modalVisibility } = storeToRefs(modalStore)

const tab = ref('login')

const schema = ref({
  name: 'required|min:3|max:100|alpha_spaces',
  email: 'required|min:3|max:100|email',
  age: 'required|min_value:18|max_value:100',
  password: 'required|min:9|max:100|excluded:password', // password ๋‹จ์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜ ์—†๋‹ค.
  confirm_password: 'confirmed:@password', // ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅํ•  ๋•Œ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌํ™•์ธ์ฐจ ์ž…๋ ฅํ•˜๋Š” ํ•„๋“œ // ์œ„์˜ password ๋ถ€๋ถ„๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•จ
  country: 'required|excluded:Antarctica',
  tos: 'required'
})

const register = (values: {
  age: number
  confirm_password: string
  country: string
  email: string
  name: string
  password: string
  tos: number
}) => {
  // VeeForm ์•ˆ์— VeeField์˜ ๋ชจ๋“  ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•ด์•ผ์ง€ values ๊ฐ’์ด ๋“ค์–ด์˜จ๋‹ค.
  // ex.
  // {
  //  age: 31
  //  confirm_password: "asdf"
  //  country: "Germany"
  //  email: "sdf@sdf.com"
  //  name: "sfdf"
  //  password: "asdf"
  //  tos: "1"
  // }
  console.log(values)
}
</script>

<template>
  <!-- Auth Modal -->
  <div class="fixed z-10 inset-0 overflow-y-auto" id="modal" :class="hiddenClass">
    <div
      class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
    >
      <div class="fixed inset-0 transition-opacity">
        <div class="absolute inset-0 bg-gray-800 opacity-75"></div>
      </div>

      <!-- This element is to trick the browser into centering the modal contents. -->
      <span class="hidden sm:inline-block sm:align-middle sm:h-screen">&#8203;</span>

      <div
        class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
      >
        <!-- Add margin if you want to see some of the overlay behind the modal-->
        <div class="py-4 text-left px-6">
          <!--Title-->
          <div class="flex justify-between items-center pb-4">
            <p class="text-2xl font-bold">Your Account</p>
            <!-- Modal Close Button -->
            <div class="modal-close cursor-pointer z-50" @click="modalVisibility = false">
              <i class="fas fa-times"></i>
            </div>
          </div>

          <!-- Tabs -->
          <ul class="flex flex-wrap mb-4">
            <li class="flex-auto text-center">
              <a
                class="block rounded py-3 px-4 transition"
                :class="{
                  'hover:text-white text-white bg-blue-600': tab === 'login',
                  'hover:text-blue-600': tab === 'register'
                }"
                href="#"
                @click.prevent="tab = 'login'"
              >
                Login
              </a>
            </li>
            <li class="flex-auto text-center">
              <a
                class="block rounded py-3 px-4 transition"
                :class="{
                  'hover:text-white text-white bg-blue-600': tab === 'register',
                  'hover:text-blue-600': tab === 'login'
                }"
                href="#"
                @click.prevent="tab = 'register'"
              >
                Register
              </a>
            </li>
          </ul>

          <!-- Login Form -->
          <form v-show="tab === 'login'">
            <!-- Email -->
            <div class="mb-3">
              <label class="inline-block mb-2">Email</label>
              <input
                type="email"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
                placeholder="Enter Email"
              />
            </div>
            <!-- Password -->
            <div class="mb-3">
              <label class="inline-block mb-2">Password</label>
              <input
                type="password"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
                placeholder="Password"
              />
            </div>
            <button
              type="submit"
              class="block w-full bg-purple-600 text-white py-1.5 px-3 rounded transition hover:bg-purple-700"
            >
              Submit
            </button>
          </form>
          <!-- Registration Form -->
          <VeeForm v-show="tab === 'register'" :validation-schema="schema" @submit="register">
            <!-- Name -->
            <div class="mb-3">
              <label class="inline-block mb-2">Name</label>
              <VeeField
                type="text"
                name="name"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
                placeholder="Enter Name"
              />
              <ErrorMessage class="text-red-600" name="name" />
            </div>
            <!-- Email -->
            <div class="mb-3">
              <label class="inline-block mb-2">Email</label>
              <VeeField
                type="email"
                name="email"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
                placeholder="Enter Email"
              />
              <ErrorMessage class="text-red-600" name="email" />
            </div>
            <!-- Age -->
            <div class="mb-3">
              <label class="inline-block mb-2">Age</label>
              <VeeField
                type="number"
                name="age"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
              />
              <ErrorMessage class="text-red-600" name="age" />
            </div>
            <!-- Password -->
            <div class="mb-3">
              <label class="inline-block mb-2">Password</label>
              <VeeField name="password" :bails="false" #default="{ field, errors }">
                <input
                  type="password"
                  class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
                  placeholder="password"
                  v-bind="field"
                />
                <div class="text-red-600" v-for="error in errors" :key="error">
                  {{ error }}
                </div>
              </VeeField>
            </div>
            <!-- Confirm Password -->
            <div class="mb-3">
              <label class="inline-block mb-2">Confirm Password</label>
              <VeeField
                type="password"
                name="confirm_password"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
                placeholder="Confirm Password"
              />
              <ErrorMessage class="text-red-600" name="confirm_password" />
            </div>
            <!-- Country -->
            <div class="mb-3">
              <label class="inline-block mb-2">Country</label>
              <VeeField
                as="select"
                name="country"
                class="block w-full py-1.5 px-3 text-gray-800 border border-gray-300 transition duration-500 focus:outline-none focus:border-black rounded"
              >
                <option value="USA">USA</option>
                <option value="Mexico">Mexico</option>
                <option value="Germany">Germany</option>
                <option value="Antarctica">Antarctica</option>
              </VeeField>
              <ErrorMessage class="text-red-600" name="country" />
            </div>
            <!-- TOS -->
            <div class="mb-3 pl-6">
              <VeeField
                type="checkbox"
                name="tos"
                value="1"
                class="w-4 h-4 float-left -ml-6 mt-1 rounded"
              />
              <label class="inline-block">Accept terms of service</label>
              <ErrorMessage class="text-red-600 block" name="tos" />
            </div>
            <button
              type="submit"
              class="block w-full bg-purple-600 text-white py-1.5 px-3 rounded transition hover:bg-purple-700"
            >
              Submit
            </button>
          </VeeForm>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped></style>














ย 



















































































































































ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย