はじめに
以下の記事で書いたコードを少しマシな感じに書き直しました。
vue.js 2.x その0004 アコーディオンを作る(jQueryのslideToggleみたいなものを作る) - Motomichi Works Blog
サーバーサイドから動的な値がwindow.itemsで書き出される想定で書いています。
rootコンポーネントから渡された値を更新したい場合は、rootコンポーネントが持っているstateを$emitなりで更新して、子供に渡しなおすと良さそうです。
そのとき、this.item.text
をwatchしたり、computedの中で使うなどしないと子供のコンポーネントの状態は更新されないと思います。たぶん。
HTMLファイルサンプル
<!DOCTYPE html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Vue.jsのアコーディオンサンプル</title> <meta charset="utf-8"> </head> <body> <style> .accordion{} .accordion__trigger{} .accordion__contents-outer-wrap{ transition: max-height 2s; max-height: 0; overflow: hidden; background: #eeeeff; } .accordion__contents-inner-wrap{ padding: 100px 10px; } </style> <div id="accordion-app"> <ul> <li is="my-component" v-for="(item, index) in state.items" :item="state.items[index]" :index="index"> </li> </ul> </div> <!-- x-templateを定義 --> <script type="text/x-template" id="my-component"> <li class="accordion"> <button class="accordion__trigger" v-on:click="slideToggle()"> accordion-trigger-{{index}} </button> <div class="accordion__contents-outer-wrap" v-bind:style="state.style"> <div class="accordion__contents-inner-wrap" ref="contentsInner"> text : {{ state.text }}<br> index : {{ index }} </div> </div> </li> </script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script> <script> // サーバーから渡されたデータなどを想定 window.items = [ { text: '何か個別に渡したいデータがあれば'}, { text: '何か個別に渡したいデータがあれば'}, ]; // my-componentを作成 Vue.component('my-component', { template: '#my-component', props: ['item', 'index'], data: function(){ return { state: { isOpen: false, text: this.item.text, style: {}, }, }; }, methods: { slideToggle: function(){ this.state.isOpen = !this.state.isOpen; this.setStyle(this.state.isOpen); }, setStyle: function(isOpen){ this.state.style = isOpen ? { 'maxHeight': this.$refs.contentsInner.clientHeight + 'px' } : {}; }, }, mounted: function(){ this.setStyle(this.state.isOpen); }, }); // VM var vueApp = new Vue({ el: '#accordion-app', data: { state: { items: window.items, }, }, }); </script> </body> </html>