Vue 예제 – 04_Tree View

04_Tree View

  • 트리 형태의 데이터
  • 컴포넌트의 재귀 호출
  • 메뉴 추가시 children 속성 추가
  • VueJS – @click, #dblclick, v-if, Custom Event, $emit, set()

Result

HTML

<!-- 테이블 템플릿 생성 -->
<script type="text/x-template" id="item-template">
    <li>
        <!-- 클릭, 더블클릭 이벤트 선언 -->
        <!-- 하위 메뉴 존재 여부에 따라 클래스 추가와 블릿 표시 -->
        <div
            :class="{bold : isFolder}"
            @click="toggle"
            @dblclick="makeFolder">
            {{ item.name }}
            <span v-if="isFolder">[{{ isOpen ? '-' : '+' }}]</span>
        </div>
        <!-- 하위 메뉴가 존재할시 표시하지만 숨김처리 -->
        <ul v-show="isOpen" v-if="isFolder">
            <!-- 템플릿 재귀 호출-->
            <!-- 호출할 커스텀 이벤트 선언 -->
            <tree-item
                class="item"
                v-for="(child,index) in item.children"
                :key="index"
                :item="child"
                @make-folder="$emit('make-folder', $event)"
                @add-item="$emit('add-item', $event)"
            >
            </tree-item>
            <!-- 아이템 추가시 emit으로 상위 이벤트 호출 -->
            <li class="add" @click="$emit('add-item', item)">+</li>
        </ul>
    </li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>

<!-- 템플릿 호출, 커스텀 이벤트 정의 -->
<ul id="demo">
    <tree-item
        class="item"
        :item="treeData"
        @make-folder="makeFolder"
        @add-item="addItem"
    >
    </tree-item>
</ul>
  • @even-name – 커스텀 이벤트, $emit으로 호출하기 위해 사용
  • $emit() – 상위 컴포넌트의 이벤트 호출

JavaScript

// 트리 데이터
var treeData = {
    name : 'My Tree',
    children : [
        { name : 'hello' },
        { name : 'wat' },
        {
            name: 'child folder',
            children: [
                {
                    name : 'child folder',
                    children : [
                        { name : 'hello' },
                        { name : 'wat' }
                    ]
                },
                { name : 'hello' },
                { name : 'wat' },
                {
                    name : 'child folder',
                    children : [
                        { name : 'hello' },
                        { name : 'wat' }
                    ]
                }
            ]
        }
    ]
}

// 컴포넌트 설정
Vue.component('tree-item', {
    template : '#item-template',
    props : {
        item : Object
    },
    data : function () {
        return {
            isOpen : false
        }
    },
    computed : {
        // 하위 메뉴 여부 체크
        isFolder : function () {
            return this.item.children &&
                    this.item.children.length
        }
    },
    methods : {
        // 하위 메뉴 표시 여부 체크
        toggle : function () {
            if (this.isFolder) {
                this.isOpen = !this.isOpen;
            }
        },
        makeFolder : function () {
            // 하위 메뉴가 없을 경우에만 실행
            if (!this.isFolder) {
                // 상위 컴포넌트의 이벤트 호출
                this.$emit('make-folder', this.item)
                this.isOpen = true
            }
        }
    }
})

var demo = new Vue({
    el : '#demo',
    data : {
        treeData : treeData
    },
    methods : {
        makeFolder : function (item) {
            // 동적으로 속성 추가
            Vue.set(item, 'children', []);
            this.addItem(item);
        },
        addItem : function (item) {
            // 메뉴 추가시 새로우 이름 설정
            item.children.push({
                name : 'new stuff'
            })
        }
    }
})
  • $emit() – 상위 컴포넌트의 이벤트 호출
  • set() – 동적으로 속성 추가
  • pust() – 배열의 요소 추가

CSS

body {
    font-family: Menlo, Consolas, monospace;
    color: #444;
    max-width:1000px;margin:0 auto
}
.item {
    cursor: pointer;
}
.bold {
    font-weight: bold;
}
ul {
    padding-left: 1em;
    line-height: 1.5em;
    list-style-type: dot;
}