102. Rendering Multiple Error Messages
npm create vue@latest
๋ค์ค ์ค๋ฅ ์ถ๋ ฅ ์ต์ ํ
- ์ง๊ธ๊น์ง๋ ํ ๊ฐ์ ์ค๋ฅ๋ง ์ถ๋ ฅํ์ง๋ง, ํน์ ์ ๋ ฅ ํ๋์์ ์ฌ๋ฌ ์ค๋ฅ๋ฅผ ์ถ๋ ฅํ ํ์๊ฐ ์์ ์๋ ์์
- ์์๋ก ๋น๋ฐ๋ฒํธ ํ๋์์์ ๋ค์ํ ๊ท์น ์๋ฐ์ ์ฌ๋ฌ ์ค๋ฅ๋ฅผ ๋์์ ์ถ๋ ฅ
๋น๋ฐ๋ฒํธ ํ๋์ ์ง์ค
- ์ฒซ๋ฒ์งธ ๋น๋ฐ๋ฒํธ ํ๋์๋ง ๊ท์น ์ ์ฉ
- ๋๋ฒ์งธ ํ๋๋ ์ฒซ๋ฒ์งธ ํ๋์ ์ผ์นํด์ผ ํ๋ฏ๋ก ๋ณ๋ ๊ท์น ๋ถํ์
๊ฒ์ฆ ์ ๋ต ๋ณ๊ฒฝ
validate
๋ ๊ธฐ๋ณธ์ ์ผ๋กfast exit
์ ๋ต์ ์ฌ์ฉํด ํ๋์ ๊ท์น์ด ์๋ฐ๋๋ฉด ๋๋จธ์ง ๊ท์น์ ๊ฒ์ฌํ์ง ์์- ์ฌ๋ฌ ์ค๋ฅ๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด
bails
์์ฑ์false
๋ก ์ค์ ํ์ฌ ๋ชจ๋ ๊ท์น์ ๊ฒ์ฌํ๋๋ก ๋ณ๊ฒฝ
์ค๋ฅ ๋ฉ์์ง ์ปดํฌ๋ํธ ์ปค์คํฐ๋ง์ด์ง:
- ๊ธฐ์กด ์ค๋ฅ ๋ฉ์์ง ์ปดํฌ๋ํธ๋ ํ ๋ฒ์ ํ๋์ ์ค๋ฅ๋ง ์ถ๋ ฅ.
- field ์ปดํฌ๋ํธ์ ์ฌ๋กฏ์ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์ค๋ฅ ๋ฉ์์ง ์ถ๋ ฅ ๊ฐ๋ฅ.
์ ๋ ฅ ํ๊ทธ์ ์ค๋ฅ ๋ฉ์์ง ๊ตฌํ:
- field ํ๊ทธ ๋ด์ input ํ๊ทธ ์ถ๊ฐ ๋ฐ ํ์ํ ์์ฑ ์ค์ .
- v-slot ์ง์๋ฌธ์ ์ฌ์ฉํ์ฌ field ์์ฑ ๊ตฌ์กฐ ๋ถํด.
- field ์์ฑ์ input ์์์ ๋ฐ์ธ๋ฉํ์ฌ ๊ฒ์ฆ ํ์ฑํ.
์ค๋ฅ ๋ฐฐ์ด ์ฒ๋ฆฌ:
- errors ๋ฐฐ์ด์ ํตํด ์ ๋ ฅ ํ๋์์ ๋ฐ์ํ๋ ์ค๋ฅ ๋ชฉ๋ก ์ ๊ทผ.
- v-for ์ง์๋ฌธ์ ์ฌ์ฉํ์ฌ ๊ฐ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋ณ๋์ ์ค๋ก ์ถ๋ ฅ.
๋น๋ฐ๋ฒํธ ๊ท์น ์ ๋ฐ์ดํธ:
- ์ต์ ๊ธ์ ์๋ฅผ 9๋ก ์ค์ .
- 'password'์ ๊ฐ์ ํน์ ๋จ์ด ์ฌ์ฉ ๊ธ์ง ๊ท์น ์ถ๊ฐ.
ํ ์คํธ ๋ฐ ํ์ธ:
- ๋ณ๊ฒฝ ์ฌํญ ์ ์ฅ ํ ํ์ด์ง ์๋ก๊ณ ์นจ.
- ๋น๋ฐ๋ฒํธ ํ๋์ 'password' ์ ๋ ฅํ์ฌ ์ฌ๋ฌ ์ค๋ฅ ๋ฉ์์ง ์ถ๋ ฅ ํ์ธ.
๋ค์ ๊ฐ์ ๊ณํ:
- ํผ์ ์ถ๊ฐ ์ต์ ํ๋ฅผ ๋ค์ ๊ฐ์์์ ๊ณ์ ์งํ ์์ .
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">​</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>
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย
ย