86. Using Getters
npm create vue@latest
이번엔 isOpen
상태 속성에 대한 게터를 만드는 것입니다.
모달이 보여야 할지 결정하는 데 사용할 것이기 때문에 하나 만들 필요가 있습니다.
다음 단계는 페이지에 모달이 나타나게 하는 것입니다.
기본적으로 모달은 보이지 않습니다.
우리는 모달에ㅐ Tailwind 클래스를 토글할 것입니다.
현재 hidden
클래스는 AppAuth
컴포넌트의 루트 요소에 있습니다.
이 클래스는 모달이 보이지 않아야 할 때 추가되어야 합니다.
기본적으로 '로그인 및 등록' 링크를 클릭하면 hidden
클래스를 추가하겠습니다.isOpen
상태 속성을 사용하여 클래스를 토글할 것입니다.
여기서 게터가 중요한 역할을 합니다.
게터는 상태 속성의 값을 검색하는 방법입니다.
스토어 파일로 전환하여 첫번째 게터를 만들어 보겠습니다.
스토어 객체 안에 getters
객체를 추가합니다.getters
객체는 게터 함수를 정의해야 하는 곳입니다.
이 객체 안에서 hiddenClass
라는 함수를 만들겠습니다.
이 함수 안에서는 값을 반환해야 합니다.
게터 함수는 반드시 값을 반환해야 합니다.
이 예제에서는 isOpen
속성이 참일 경우 요소에 적용될 hidden
클래스를 반환해야 합니다.
그렇지 않으면 빈 문자열을 반환합니다.
모든 게터 함수는 매개변수로 상태를 제공받습니다.
상태를 받아 다음 값을 반환하세요:
!state.isOpen ? 'hidden' : ''
게터 함수가 준비되었습니다.
게터 함수는 애플리케이션의 컴포넌트에 노출됩니다.
다음 단계는 인증 컴포넌트 내에서 게터 함수를 사용하는 것입니다.
인증 컴포넌트로 전환해봅시다.
게터 함수는 스토어와 유사하게 계산된 속성으로 추가해야 합니다.
컴포넌트의 계산된 속성에 게터 함수를 매핑할 수 있습니다.
먼저 스크립트 블록에서 모달 스토어를 가져옵니다.useModalStore
라는 이름으로 모달 스토어를 가져옵니다.
그런 다음에는 Pinia 패키지에서 mapState
함수를 가져옵니다.mapState
함수는 게터 함수를 매핑할 수 있으며, 상태 속성에만 국한되지 않습니다.
컴포넌트 옵션의 computed
객체에서 mapState
함수를 확산 연산자와 함께 호출합니다.
이 함수에는 모달 스토어를 전달하면 됩니다.
마지막으로, 게터 함수의 이름인 hiddenClass
를 배열에 추가합니다.
이로써 게터 함수가 우리 컴포넌트에 노출되었습니다.hiddenClass
computed
속성을 컴포넌트에 직접 정의할 수도 있었지만, 로직을 스토어에 외부화함으로써 이 함수를 어떤 컴포넌트에서든 매핑할 수 있습니다.
그렇지 않았다면 이 함수는 AppAuth
컴포넌트에서만 사용 가능했을 것입니다.
이제 이 computed
속성을 모달에 연결하려고 합니다.
템플릿 블록으로 스크롤하여 modal
이라는 id를 가진 div 요소에 클래스 속성을 바인딩하겠습니다.
이 클래스 첫번째 클래스 속성과 별도로 설정됩니다.
클래스 속성들은 병합될 것입니다.
우리는 다음 조건으로 클래스를 설정할 것입니다.hiddenClass
이제 이것이 작동하는지 확인해보겠습니다.
브라우저로 전환합니다.
로그인 버튼을 클릭하면 모달이 나타날 것입니다.
(아직 닫히진 않습니다.)
<script setup lang="ts">
import AppHeader from '@/components/AppHeader.vue'
import AppAuth from '@/components/AppAuth.vue'
</script>
<template>
<AppHeader />
<!-- Introduction -->
<section class="mb-8 py-20 text-white text-center relative">
<div
class="absolute inset-0 w-full h-full bg-contain introduction-bg"
style="background-image: url('assets/img/header.png')"
></div>
<div class="container mx-auto">
<div class="text-white main-header-content">
<h1 class="font-bold text-5xl mb-5">Listen to Great Music!</h1>
<p class="w-full md:w-8/12 mx-auto">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus et dolor mollis, congue
augue non, venenatis elit. Nunc justo eros, suscipit ac aliquet imperdiet, venenatis et
sapien. Duis sed magna pulvinar, fringilla lorem eget, ullamcorper urna.
</p>
</div>
</div>
<img
class="relative block mx-auto mt-5 -mb-20 w-auto max-w-full"
src="/assets/img/introduction-music.png"
/>
</section>
<!-- Main Content -->
<section class="container mx-auto">
<div class="bg-white rounded border border-gray-200 relative flex flex-col">
<div class="px-6 pt-6 pb-5 font-bold border-b border-gray-200">
<span class="card-title">Songs</span>
<!-- Icon -->
<i class="fa fa-headphones-alt float-right text-green-400 text-xl"></i>
</div>
<!-- Playlist -->
<ol id="playlist">
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
<li
class="flex justify-between items-center p-3 pl-6 cursor-pointer transition duration-300 hover:bg-gray-50"
>
<div>
<a href="#" class="font-bold block text-gray-600">Song Title</a>
<span class="text-gray-500 text-sm">Artist Name</span>
</div>
<div class="text-gray-600 text-lg">
<span class="comments">
<i class="fa fa-comments text-gray-600"></i>
15
</span>
</div>
</li>
</ol>
<!-- .. end Playlist -->
</div>
</section>
<!-- Player -->
<div class="fixed bottom-0 left-0 bg-white px-4 py-2 w-full">
<!-- Track Info -->
<div class="text-center">
<span class="song-title font-bold">Song Title</span> by
<span class="song-artist">Artist</span>
</div>
<div class="flex flex-nowrap gap-4 items-center">
<!-- Play/Pause Button -->
<button type="button">
<i class="fa fa-play text-gray-500 text-xl"></i>
</button>
<!-- Current Position -->
<div class="player-currenttime">00:00</div>
<!-- Scrub Container -->
<div class="w-full h-2 rounded bg-gray-200 relative cursor-pointer">
<!-- Player Ball -->
<span class="absolute -top-2.5 -ml-2.5 text-gray-800 text-lg" style="left: 50%">
<i class="fas fa-circle"></i>
</span>
<!-- Player Progress Bar-->
<span
class="block h-2 rounded bg-gradient-to-r from-green-500 to-green-400"
style="width: 50%"
></span>
</div>
<!-- Duration -->
<div class="player-duration">03:06</div>
</div>
</div>
<AppAuth />
</template>
<script setup lang="ts">
import useModal from '@/stores/modal'
import { storeToRefs } from 'pinia'
const modalStore = useModal()
const { isOpen } = storeToRefs(modalStore)
const toggleAuthModal = () => {
isOpen.value = !isOpen.value
console.log(isOpen.value)
}
</script>
<template>
<!-- Header -->
<header id="header" class="bg-gray-700">
<nav class="container mx-auto flex justify-start items-center py-5 px-4">
<!-- App Name -->
<a class="text-white font-bold uppercase text-2xl mr-4" href="#">Music</a>
<div class="flex flex-grow items-center">
<!-- Primary Navigation -->
<ul class="flex flex-row mt-1">
<!-- Navigation Links -->
<li>
<a class="px-2 text-white" href="#" @click.prevent="toggleAuthModal"
>Login / Register</a
>
</li>
<li>
<a class="px-2 text-white" href="#">Manage</a>
</li>
</ul>
</div>
</nav>
</header>
</template>
<style scoped></style>
<script setup lang="ts">
import useModal from '@/stores/modal'
import { storeToRefs } from 'pinia'
const modalStore = useModal()
const { hiddenClass } = storeToRefs(modalStore)
</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">
<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 hover:text-white text-white bg-blue-600"
href="#"
>Login</a
>
</li>
<li class="flex-auto text-center">
<a class="block rounded py-3 px-4 transition" href="#">Register</a>
</li>
</ul>
<!-- Login Form -->
<form>
<!-- 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 -->
<form>
<!-- Name -->
<div class="mb-3">
<label class="inline-block mb-2">Name</label>
<input
type="text"
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"
/>
</div>
<!-- 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>
<!-- Age -->
<div class="mb-3">
<label class="inline-block mb-2">Age</label>
<input
type="number"
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"
/>
</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>
<!-- Confirm Password -->
<div class="mb-3">
<label class="inline-block mb-2">Confirm 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="Confirm Password"
/>
</div>
<!-- Country -->
<div class="mb-3">
<label class="inline-block mb-2">Country</label>
<select
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>
</select>
</div>
<!-- TOS -->
<div class="mb-3 pl-6">
<input type="checkbox" class="w-4 h-4 float-left -ml-6 mt-1 rounded" />
<label class="inline-block">Accept terms of service</label>
</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>
</div>
</div>
</div>
</div>
</template>
<style scoped></style>
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
const useModal = defineStore('modal', () => {
const isOpen = ref(false)
const hiddenClass = computed(() => (!isOpen.value ? 'hidden' : ''))
return {
isOpen,
hiddenClass
}
})
export default useModal