<section v-for="post in posts" :key="post">
<div>
<ul @click="[loadDetail(post.postSeq), toggleAccordion(post.postSeq)]">
...
<li style="width:220px">{{setDate(post.postTime)}}</li>
</ul>
</div>
<transition name="detail" mode="out-in">
<div v-if=post.view>
<div class="txt-textarea">
<textarea
:ref="getContentRef(post.postSeq)"
disabled="isEdit"
v-model="check_detailData.content"
:style="{height:textAreaHeight+'px'}"
></textarea>
...
여기서 사용되는 코드는 loadDetail이라는 클릭 시 => async로 데이터를 로딩하는 함수이다.
toggleAccordion은 위 사진처럼 다른 요소를 클릭하면 클릭한 요소를 제외한 나머지 다른 요소는 숨겨주는 함수이다.
마지막으로 해당 testarea에 대한 값을 ref를 v-bind로 이어주고 해당 요소에 값을 적용할 수 있도록 동적 styling을 부여하면 된다.
const textAreaHeight = ref(0)
const contentRefs = ref([])
vue의 transiton에는 before-enter라는 directive가 존재하나 이상하게 나는 적용되지 않았다. 그래서 ref를 이용하여 확실히 해당요소에 대한 style의 높이값을 부여하였다.
이에 우선 되어야할 것은 @click 이벤트 시 현재 대상의 ref를 가져오는 것이 최우선이다.
const contentRefs = ref({null})
const getContentRef = (id) => {
return (el) => {
if(el){
contentRefs[id].value = el;
} else {
contentRefs[id].value = null;
}
};
}
const setHeight = (idx) => {
const contentElement = contentRefs[idx].value;
if (contentElement) {
textAreaHeight.value = contentElement.scrollHeight.value;
}else{
textAreaHeight.value = 0;
}
},
async loadDetail(postSeq) {
textAreaHeight.value = 0;
try {
await 상태변경함수||dispatch함수;
setHeight(postSeq);
} catch (err) {
console.log(err.message);
}
},
여기서 이전의 textAreaHeight값이 적용되는 것을 방지하고자 0으로 초기화 해주고
await 함수 다음에 setHeight으로 해당 로직이 실행한 후 동작할 수 있도록 한다.
이때 async 할 dispatch함수가 없다면 toggle accordion 같은 상태 변경함수를 await으로 실행시킨 후 작성해도 된다.
.detail-enter-from,
.detail-leave-to {
opacity: 0;
height: 0px;
}
.detail-enter-active {
transition: all 0.2s ease-out;
}
.detail-leave-active {
transition: all 0.2s ease-in;
}
.detail-enter-to,
.detail-leave-from {
opacity: 1;
}
<template>
<div>
<div
v-for="item in menuItems"
:key="item.id"
class="accordion-item"
:class="{ active: item.isActive }"
>
<div class="accordion-header" @click="toggleItem(item)">
{{ item.title }}
</div>
<transition name="accordion-slide">
<div v-if="item.isActive" class="accordion-content" :style="{ height: item.contentHeight + 'px' }" :ref="getContentRef(item.id)">
{{ item.content }}
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
data() {
return {
menuItems: [
{
id: 1,
title: '메뉴 1',
content: '메뉴 1의 내용',
isActive: false,
contentHeight: 0
},
{
id: 2,
title: '메뉴 2',
content: '메뉴 2의 내용',
isActive: false,
contentHeight: 0
},
{
id: 3,
title: '메뉴 3',
content: '메뉴 3의 내용',
isActive: false,
contentHeight: 0
}
],
contentRefs: {} // ref를 저장할 객체
};
},
methods: {
toggleItem(item) {
item.isActive = !item.isActive;
this.updateContentHeight(item);
},
updateContentHeight(item) {
const contentElement = this.contentRefs[item.id];
if (contentElement) {
item.contentHeight = contentElement.scrollHeight;
}
},
getContentRef(id) {
return (el) => {
this.contentRefs[id] = el;
};
}
}
};
</script>
<style>
.accordion-item {
border: 1px solid #ccc;
margin-bottom: 10px;
}
.accordion-header {
cursor: pointer;
padding: 10px;
}
.accordion-content {
padding: 10px;
background-color: #f5f5f5;
overflow: hidden;
}
.accordion-slide-enter-active,
.accordion-slide-leave-active {
transition: height 0.3s;
}
.accordion-slide-enter,
.accordion-slide-leave-to {
height: 0;
overflow: hidden;
}
</style>
<transition name="detail" @after-enter="setHeight" @after-leave="resetHeight">
<div v-if="showDetail" class="detailBox">
<!-- Details go here -->
</div>
</transition>
methods: {
setHeight(el) {
el.style.height = 'auto';
},
resetHeight(el) {
el.style.height = null;
}
}