BrownBe
BrownBe

Reputation: 987

Vue js : Call a child method from the parent component

I tried to make communicate to components with vuejs and vuex :

  1. First component is a menu
  2. Second component, inside the first, is an hamburgerButton.

In this very current case, when I click the hamburgerButton, this one is animated and the menu too. With this code, after the button click, the menu is animated with vueTransition and the hamburgerButton too. Note that i use vuex to manage a menuIsOpen state.

My problem is when i click an item of the menu, I would like fired the hamburgerButton animation.

hamburgerButtonanimation method, call animButtonHandler(), is encapsulated in a @click event. Actually i understand why it doesn't work right know, but I don't perceive how to handle this method to the click of a Parent element (item of the menu). So my question is, how can I access a method to a child component from a parent component ? Or is there an another workaround methodology to achieve this ?

parent component - menu.vue :

<template>
    <div class="menu">
        <!-- import hamburgerButton component -->
        <hamburger-button></hamburger-button>
        <transition v-on:enter="enterMenuHandler" v-on:leave="leaveMenuHandler">
            <div class="menu_wrapper" v-if="this.$store.state.menuIsOpen">
                <ul class="menu_items">
                    <li class="menu_item" @click="$store.commit('toggleMenu')">
                        <router-link class="menu_link" to="/">home</router-link>
                        <router-link class="menu_link" to="/contact">contact</router-link>
                    </li>
                </ul>
            </div>
        </transition>
    </div>
</template>

<script>
import hamburgerButton from "hamburgerButton.vue";

export default {
    components: {
        'hamburger-button': hamburgerButton,
    },
    methods: {
        enterMenuHandler(el, done){
            TweenLite.fromTo(el, 0.5, {
                opacity: '0',
            },{
                opacity: '1',
                onComplete: done
            });
        },
        leaveMenuHandler(el, done){
            TweenLite.to(el, 0.5, {
                opacity: '0',
                onComplete: done
            });
        },
    }
}
</script>

child component : hamburgerButton.vue :

<template>
    <div class="hamburgerButton" @click="animButtonHandler()">
        <div class="hamburgerButton_inner" ref="hamburgerButtonInner">
            <i class="hamburgerButton_icon></i>
        </div>
    </div>
</template>

<script>
export default {
    methods: {
        animButtonHandler (){
            // toggle the state of menu if button clicked
            this.$store.commit('toggleMenu');
            const isOpen = this.$store.state.menuIsOpen === true;
            // anim the button
            TweenLite.to(this.$refs.hamburgerButtonInner, 0.5, {
                rotation: isOpen ? "43deg" : '0',
            });
        },
    }
}
</script>

store.js (imported in the main.js) :

let store = new Vuex.Store({
    state : {
            menuIsOpen : false,
    },
    mutations: {
        toggleMenu(state) {
            state.menuIsOpen = !state.menuIsOpen
         }
    }
});

Upvotes: 1

Views: 1143

Answers (2)

Hardik Satasiya
Hardik Satasiya

Reputation: 9693

I have added basic Example of event bus. you can now compare it with and do changes like wise.

if find any difficulties please comment.

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div id="app">
    <h2>event bus</h2>    
    <button @click="callChildAnimateMethod"> Button On Parent Call Child </button>     
    <childcmp><childcmp />
  </div>
  <script>  
  var EventBus = new Vue();
  
  Vue.component('childcmp', {
    template: `<div>child demo - {{ message }}</div>`,
    data: function() {
      return {
        message: 'hello'
      }
    },
    mounted: function() {
      // listen for event
      EventBus.$on('animate', this.animButtonHandler);
    },
    destroyed: function(){
      // remove listener
      EventBus.$off('animate', this.animButtonHandler);
      
    },
    methods: {
      animButtonHandler: function() {
        console.log('this is child method');
        this.message = 'I am changed.'
      }
    }
  });

   new Vue({
      el: '#app',
      data: function() {
        return {
          
        }
      },
      methods: {
        callChildAnimateMethod: function() {          
          EventBus.$emit('animate');
        }
      }

    });
  </script>
</body>
</html>

Update


you need to define EventBus

eventbus.js

import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;

parent component - menu.vue

import EventBus from './eventbus.js'
... your code

child component : hamburgerButton.vue :

import EventBus from './eventbus.js'
... your code

now EventBus will be available to your code.

Upvotes: 1

Vamsi Krishna
Vamsi Krishna

Reputation: 31352

Since you wanted to know how to imtegrate event bus with your code, here it is:

Create an event bus which is just an empty vue instance

Add it in you main.js file or outsource it in a spererate file;

main.js

export const EventBus = new Vue();

menu.vue

<template>
    <div class="menu">
        <!-- import hamburgerButton component -->
        <hamburger-button></hamburger-button>
        <transition v-on:enter="enterMenuHandler" v-on:leave="leaveMenuHandler">
            <div class="menu_wrapper" v-if="this.$store.state.menuIsOpen">
                <ul class="menu_items">
                    <li class="menu_item" @click="toggleMenu">
                        <router-link class="menu_link" to="/">home</router-link>
                        <router-link class="menu_link" to="/contact">contact</router-link>
                    </li>
                </ul>w
            </div>
        </transition>
    </div>
</template>

<script>
import hamburgerButton from "hamburgerButton.vue";
import {EventBus} from './path/to/main.js' //or a path to file where you exported your EventBus

export default {
    components: {
        'hamburger-button': hamburgerButton,
    },
    methods: {
        toggleMenu(){
            this.$store.commit('toggleMenu');
            EventBus.$emit('animate-hamburger-btn');
        },
        enterMenuHandler(el, done){
            TweenLite.fromTo(el, 0.5, {
                opacity: '0',
            },{
                opacity: '1',
                onComplete: done
            });
        },
        leaveMenuHandler(el, done){
            TweenLite.to(el, 0.5, {
                opacity: '0',
                onComplete: done
            });
        },
    }
}
</script> 

set up a event listener on the event bus in the created hook and perform the animation on every animate-hamburger-btn event

hamburgerButton.vue

<template>
    <div class="hamburgerButton" @click="animButtonHandler()">
        <div class="hamburgerButton_inner" ref="hamburgerButtonInner">
            <i class="hamburgerButton_icon></i>
        </div>
    </div>
</template>

<script>
import {EventBus} from './path/to/main.js' //or a path to file where you exported your EventBus
export default {
    created(){
        EventBus.$on('animate-hamburger-btn', () => {
            this.animateBtn();
        });
    }.
    methods: {
        animButtonHandler (){
            // toggle the state of menu if button clicked
            this.$store.commit('toggleMenu');
            this.animateBtn();
        },
        animateBtn(){
            const isOpen = this.$store.state.menuIsOpen === true;
            // anim the button
            TweenLite.to(this.$refs.hamburgerButtonInner, 0.5, {
                rotation: isOpen ? "43deg" : '0',
            });
        }
    }
}
</script> 

Upvotes: 1

Related Questions