slot에 대해 배웠으니, BadgeList.vue에서도 base-card 슬롯을 사용할 수 있다. BadgeList.vue에도 card look을 적용할 section이 있으므로.
<!--BadgeList.vue-->
<template>
<!--<section>-->
<base-card>
<h2>Available Badges</h2>
<ul>
<li>
<base-badge type="admin" caption="ADMIN"></base-badge>
</li>
<li>
<base-badge type="author" caption="AUTHOR"></base-badge>
</li>
</ul>
</base-card>
<!--</section>-->
</template>
<style scoped>
/* section {
margin: 2rem auto;
max-width: 30rem;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
padding: 1rem;
} */
section h2 {
margin: 0.5rem 0;
color: #3a3a3a;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
}
li {
margin-right: 1rem;
}
</style>
BadgeList에서 section 스타일은 삭제하고, base-card 커스텀 태그를 이용해 h2태그와 ul을 wrapping하자.
base-card를 컨텐츠 wrapper로 사용하면, 이전과 동일하게 화면에 표시된다.
이제 재사용할 수 있는 card 컴포넌트가 생겼다. card look이 필요하면 언제든지 사용할 수 있다.
# 여러 개의 slot
slot의 용도가 이게 전부는 아니다.
컴포넌트의 여는 태그와 닫는 태그 사이에 있는 '동적 콘텐츠'를 전달할 수 있는건 좋은데, 가끔은 커스텀 컴포넌트에 여러 개의 slot이 있어야하는 상황도 있다.
BaseCard.vue 파일을 보면, 지금은 한 개의 slot이 있지만 두 개의 slot이 필요할 수도 있다.
header용 slot 하나, 메인 컨텐츠용 slot 하나라고 해보자.
BaseCard.vue에서 header 태그를 추가하여 모든 card에 대해 항상 동일한 구조를 갖도록 해보자.
header태그에도 slot을 추가한다.
<template>
<div>
<header>
<slot></slot>
</header>
<slot></slot>
</div>
</template>
이제 slot이 두 개가 있으므로, Vue는 부모 컴포넌트로부터 제공된 컨텐츠가 이 두 slot 중, 어디로 가야 하는지 알 수 없게 된다.
## 복수의 slot 사용방법
그래서 두 개 이상의 slot을 사용하는 경우, slot 요소에 대한 name 속성을 사용해서 slot에 이름을 추가할 수 있다.
여기에선 이름을 header로 지정해보자.
- 모든 slot의 이름을 지정할 필요는 없다. 이름이 없는 slot을 하나 남겨두면, 그게 default slot이 된다.
- 대신, 이름이 없는 slot은 하나만 있어야 한다.
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<slot></slot>
</div>
</template>
이제 컨텐츠를 다른 slot에 할당할 수 있다.
예를 들어, 아래 UserInfo.vue에서 h3 태그와 base-badge 태그가 BaseCard.vue 컴포넌트의 <header> slot으로 가야한다고 Vue에 지시해보자.
<!--UserInfo.vue-->
<template>
<section>
<base-card>
<header>
<h3>{{ fullName }}</h3>
<base-badge :type="role" :caption="role.toUpperCase()"></base-badge>
</header>
<p>{{ infoText }}</p>
</base-card>
</section>
</template>
▼
<template>
<section>
<base-card>
<template v-slot:header>
<h3>{{ fullName }}</h3>
<base-badge :type="role" :caption="role.toUpperCase()"></base-badge>
</template>
<p>{{ infoText }}</p> <!--이 부분은 자동으로 기본 slot으로 이동.-->
</base-card>
</section>
</template>
<script scoped>
export default {
props: ["fullName", "infoText", "role"],
};
</script>
<style scoped>
section header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
- 기본 HTML 요소인 template태그로 wrapping한 다음, name 슬롯에 들어갈 컨텐츠가 포함된 template 태그에 특수 디렉티브인 v-slot 디렉티브를 추가한다.
- v-slot 디렉티브를 사용해서 Vue에게 특정 컨텐츠가 어디로 가야할지를 알려줄 수 있다.
- Vue에 컨텐츠가 가야할 위치를 지시하려면 v-slot 디렉티브에 인수를 추가하면 된다.
- :을 입력한 다음, slot의 이름을 추가한다. BaseCard.vue에는 "header"라는 slot의 이름으로 지정된 slot이 있으므로 header를 인수로 추가할 수 있다.
<slot name="header"></slot>
- template 내부의 컨텐츠는 이 이름의 slot으로 간다고 Vue에 지시하는 것.
- 참고로, template은 기본 HTML 태그이다. 화면에는 아무것도 렌더링하지않기 때문에 여기에서는 컨테이너로 사용한다.
- 'v-slot이 있는 template 내부'에 포함되지 않은 컨텐츠는 자동으로 기본 slot으로 이동한다.(위 코드에서는 p 태그)
이걸 저장하면 이전과 거의 같아 보이지만 바뀐 것이 있다. 스타일이 변경되었다.
배지가 이름 옆에 표시되지 않는다. 왜냐하면 UserInfo.vue 컴포넌트에 더이상 header 태그가 없으므로.
대신, h3 이하 컨텐츠를 BaseCard.vue의 header로 보내는 template이 있을 뿐.
이름 옆에 배지를 배치하는걸 담당하는 header 스타일링은 아직 UserInfo.vue에 있고, 스타일링의 범위가 해당 컴포넌트로 지정되어 있어(scoped) BaseCard.vue에 있는 header에는 영향을 주지 않는다.
## v-slot:default
BadgeList.vue 컴포넌트도 코드를 변경해보자.
BadgeList.vue 컴포넌트는 다음과 같다.
<template>
<base-card>
<h2>Available Badges</h2>
<ul>
<li>
<base-badge type="admin" caption="ADMIN"></base-badge>
</li>
<li>
<base-badge type="author" caption="AUTHOR"></base-badge>
</li>
</ul>
</base-card>
</template>
여기서 h2 태그를 "header"인 slot으로 보내자.
그리고, 남아있는 컨텐츠 (ul 태그 이하)는 기본 슬롯으로 이동한다는 사실을 좀 더 분명히 표시하기 위해, 해당 컨텐츠를 template 태그로 wrapping한 다음, v-slot:default를 추가해준다.
- 참고로, default는 예악어(reserved word)이다.
- 이름에서 알 수 있듯이 항상 기본 slot을 대상으로 한다.
<!--BadgeList.vue-->
<template>
<base-card>
<template v-slot:header> <!--Basecard컴포넌트의 headert슬롯으로 이동-->
<h2>Available Badges</h2>
</template>
<template v-slot:default> <!--Basecard컴포넌트의 기본 슬롯으로 이동-->
<ul>
<li>
<base-badge type="admin" caption="ADMIN"></base-badge>
</li>
<li>
<base-badge type="author" caption="AUTHOR"></base-badge>
</li>
</ul>
</template>
</base-card>
</template>
UserInfo.vue 컴포넌트에서도 어느 컨텐츠가 어디로 가는지 명확하게 하기 위해 v-slot:default값을 추가하여 동일하게 작업을 해준다.
<!--UserInfo.vue-->
<template>
<section>
<base-card>
<template v-slot:header>
<h3>{{ fullName }}</h3>
<base-badge :type="role" :caption="role.toUpperCase()"></base-badge>
</template>
<template v-slot:default>
<p>{{ infoText }}</p>
</template>
</base-card>
</section>
</template>
** 출처: 모든 내용은 Udemy Vue-완벽가이드 강의를 기반으로 작성하였습니다.
'Vue.js' 카테고리의 다른 글
[Udemy Vue 완벽가이드 Section9] 116. 슬롯에 대한 추가 정보(fallback 기본컨텐츠, $slots, #default) (0) | 2023.08.30 |
---|---|
[Udemy Vue 완벽가이드 Section9] 115. 슬롯 스타일 및 컴파일 (0) | 2023.08.30 |
[Udemy Vue 완벽가이드 Section9] 113. 슬롯(slots) 소개 (0) | 2023.08.29 |
[Udemy Vue 완벽가이드 Section9] 112. 범위가 지정된(scoped) 스타일 (0) | 2023.08.29 |
[Udemy Vue 완벽가이드 Section9] 111. 전역 컴포넌트와 지역 컴포넌트 (0) | 2023.08.29 |