How to emit and listen to events between components on different routes

The navigation component remains fixed on all views. On the text field present in navigation comp, I search for something(data->keyword) and on button click gets routed to a different page/component B. I also emit a global event which has the input’s value(keyword) before navigating programmatically to component B.

My Navigation component A which is fixed.

      <div class="col-11 pr-0">
        <input class="form-control" type="text" placeholder="Search" v-
             model="keyword">
      </div> 
      <div class="col-1 p-0">
         <span class="d-inline" @click="toSearchListing()"><button class="btn">Find</button</span>
      </div>

script :

   import {EventBus} from '../event-bus';
   export default {
     name: 'navbar',
    data(){
       return{
            keyword:''
       }
   },
   methods:{
          toSearchListing(){
              if(this.$route.path !== '/searchPage'){
       
                 EventBus.$emit('keywordQuery',this.keyword); 
        
                  this.$router.push('/searchPage');
              }

              else{

                  EventBus.$emit('keywordQuery',this.keyword);

              }
           }
        }
  }

Component B

<listing  v-for="(item,index) in listingData" :key="item.reg_id" v-bind:random="item"></listing>

SCRIPT:

  export default {

  name: 'search-listing-component',

  components:{searchFilter,listing},
  data(){

  	return {

  		listingData : [],
  		showSortBtn:false

  	}
  },
  created(){

  	console.log('creatted');	

  	let main = this; 	

  	EventBus.$on('keywordQuery',function(queryValue){
  		
  		Axios.post('/api/v1/search',{
                 'keyword' : queryValue
                }).then(function(response){

                    main.listingData.push.apply(main.listingData,response.data.results);	              
                 }, function(error){
              	      alert(error);
                })
         
  	});
}
  }

The problem is that I am able to retrieve and display the data from the AXIOS request only if component A (Navigation bar) and Component B(listing) are in the same page (URL : /searchPage).

If I am in some other page (URL:/someUrl), then on button click, I get routed to /searchPage (as expected). My event(AXIOS request) gets triggered/listened. But if I console out the value of listingData outside the event listener, it shows empty. So I am not able to display it to the listing component.

I saw some stack overflow answer suggesting to wrap “keep-alive” around the <router-view> elements. Surprisingly, It worked. What purpose does keep alive serve and what was I missing. I am a newbie to Vue-js, so pardon me if it were a silly doubt.

I’m using Vue-js 2.0

1 Like

You just came across one of the drawbacks of using an event bus.

My honest recommendation is to not use it, and use a proper state management solution. Getting this to work with an event bus will result in a hacky solution that will inevitably break later when you change something else.

When you say “proper state management solution” are you referring to Vuex?

I’m new to frontend and want to avoid any problems like above with my next project. But I am not sure if Vuex could solve the problems mentioned in the OP.

Hi @LinusBorg. Thanks for the suggestion. Would definitely go with Vuex for the long run. But I would like to know what’s happening in the above scenario. Since I have just started with Vue, it would make me appreciate and understand Vuex in a better way.

Vuex avoids those problems mentioned by OP all together.

Couple things. On every creation of your components, you create new event listeners on your EventBus, annoying to manually manage. You do not destroy. That means when you switch pages, you have an event listener hanging onto code that cant be garbaged collected. When you do this stuff you have to manually remove your event listener on component destruction. Besides these annoying eventbusses as @LinusBorg has mentioned, other possible issues here are.

Vue-Router only creates your component when you are accessing said route. On Component A, you emit, then you go to another page. But you are essentially emitting to no where. The component is not alive so it goes no where.

Your keep alive component does just that, makes sure that the component is kept alive even when you navigate away. However this may be fine, but is not a solution to this general problem. Keeping a component alive when it isnt used, can be useless on resources / processing, and if you are using events, you could easily find your self in hard to debug situations where a component that is alive and hidden, starts to affect your events that you emit and then you start pulling out your hair because you forgot that you have several components existing in memory and you dont know where these events are being consumed. Not a fun thing. Again, event buses are generally NOT recommended with a data/state management library like Vue. (There are edge cases)

What you want is Vuex. A central, singleton store, that is accessible to all components at any given time. Instead of listening to events, you want to react to data changes. Vue is all based on state, and data. Not dom nore “events” in the classical sense like this.

5 Likes

Couldn’t have said it better.

1 Like

Thanks for the detailed explanation. However, one thing is that my events listen. If it were a component alive issue, then my events wouldn’t get listened right? The problem I face is that the data is not getting updated. If I console out the response from the AXIOS request inside the event listener, it is working fine. Any idea why?

So in my case, I just have doneEvents array that I want to show in different route /done-events-list nothing complicated.
Any suggestion?
I am using the EventBus . It works when I have my Calendar and DoneEventsList components inside App.vue
But, I want to just transfer the list from Calendar to DoneEventList by using eventbus and not going through setting up the vuex even though what you guys have said totally make sense