JavaScript
vue.js

v-for内の子コンポーネントのメソッドを親から$refsで呼ぶときにエラーが出てしまうときの対処法

やりたいこと

  • v-forの中で子コンポーネントを複数作る
  • 親コンポーネントからそれぞれの子コンポーネントのメソッドを呼びたい

前提

親コンポーネントから子コンポーネントのメソッドを呼びたい時、$refsを使えば可能です。

App.vue
    //親コンポーネント
    <template>
        <div id="app">
            <button @click="callChildMethod">Change Message</button>
            <!-- 子コンポーネントにref属性を付けて参照出来るようにする -->
            <HelloWorld ref="child"/>
        </div>
    </template>

    <script>
    import HelloWorld from './components/HelloWorld'

    export default {
        name: 'App',
        methods: {
            callChildMethod(){
                //子コンポーネントのメソッドを呼ぶ
                this.$refs.child.changeMsg();
            }
        },
        components: {
            HelloWorld
        }
    }
    </script>
HelloWorld.vue
    //子コンポーネント
    <template>
        <div class="hello">
            <p>{{ msg }}</p>
        </div>
    </template>

    <script>
    export default {
        name: 'HelloWorld',
        data () {
            return {
                msg: 'Hello!'
            }
        },
        methods: {
            changeMsg(){
                this.msg = 'Hi!';
            }
        }
    }
    </script>

v-for内の場合

しかし、v-for内でこの手法を使おうとするとUncaught TypeError: this.$refs.child.changeMsg is not a functionとエラーが出てしまいます。

App.vue
    <template>
        <div id="app">
            <!-- 子コンポーネントをv-forで複数個作る -->
            <div v-for="n in 3" :key="n">
                <button @click="callChildMethod">Change Message</button>
                <HelloWorld ref="child"/>
            </div>
        </div>
    </template>

    <script>
    import HelloWorld from './components/HelloWorld'

    export default {
        name: 'App',
        methods: {
            callChildMethod(){
                this.$refs.child.changeMsg();
                //→Uncaught TypeError: this.$refs.child.changeMsg is not a function
            }
        },
        components: {
            HelloWorld
        }
    }
    </script>

対処法

その場合はv-forの引数として提供されているindexを渡してあげれば解決します。

App.vue
    <template>
        <div id="app">
            <!-- v-forの引数でindexを指定 -->
            <div v-for="(n, index) in 3" :key="n">
                <!-- メソッドにindexを渡す -->
                <button @click="callChildMethod(index)">Change Message</button>
                <HelloWorld ref="child"/>
            </div>
        </div>
    </template>

    <script>
    import HelloWorld from './components/HelloWorld'

    export default {
        name: 'App',
        methods: {
            callChildMethod(index){
                //this.$refs.参照ID[index].子のメソッド()の形で呼ぶ
                this.$refs.child[index].changeMsg();
            }
        },
        components: {
            HelloWorld
        }
    }
    </script>

vue_component.gif

注意点

ただしコンポーネント同士が密結合になってしまい良くないので、あくまで小規模なものの中で使うようにして、大規模なものに関してはvuexを使うようにした方が後々幸せになれるかもしれません。

参考

https://laracasts.com/discuss/channels/vue/child-and-refs-not-a-function