Reg
Reg

Reputation: 77

Vue 2 component re-render after switching dynamic component inside it

I have a main component that import two other components:

  1. admin page
  2. login page

in the main component I have:

<template>
  <div id="app">
    <component v-bind:is="currentView"></component>
  </div>
</template>

<script>
import AdminPage from './components/adminPage/AdminPage'
import LoginPage from './components/LoginPage'

export default {
  name: 'app',
  components: {
    AdminPage, LoginPage
  },
  data: function() {
    return {
      currentView: 'LoginPage'
    }
  },
  created() {
       this.$bus.$on('eventFromLoginPage', event => {
            this.currentView = "AdminPage";
        });
  }
}
</script> 

and in the Login Page I have a method that emit a trigger to the main app:

changeView: function() {
       this.$bus.$emit('eventFromLoginPage');
 }

The main component is called by the main.js file:

import Vue from 'vue'
import App from './App'

Object.defineProperty(Vue.prototype, '$bus', {
    get() {
        return this.$root.bus;
    }
});

var bus = new Vue({})

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App },
  data: {
    bus: bus
  }
})

The problem is that although when I call the changeView function in the login page, the trigger is sent and the view in the main component changes to the adminPage as desired, but than the main page is re rendered and the view returns to the login page.

The question: Why does the main component re-render after changing the "currentView" state and how can I keep the Admin page as the view.

Upvotes: 1

Views: 2213

Answers (1)

Roy J
Roy J

Reputation: 43899

  • Calling Vue on something inside a template is weird. Call it on the top-level HTML element.
  • Only use a bus when events are being handled by something outside the parent-chain of the component emitting the event
  • Handle events using v-on and a method

AdminPage = {
  template: '<div>The admin page</div>'
};

LoginPage = {
  template: '<div>The LOGIN page<button @click="doLogin">Login</button></div>',
  methods: {
    doLogin: function() {
      this.$emit('eventFromLoginPage');
    }
  }
};

App = {
  template: '<component v-bind:is="currentView" v-on:eventFromLoginPage="goToAdminPage"></component>',
  components: {
    AdminPage, LoginPage
  },
  data: function() {
    return {
      currentView: 'LoginPage'
    }
  },
  methods: {
    goToAdminPage: function() {
      this.currentView = 'AdminPage';
    }
  }
};

new Vue({
  el: '#app',
  components: {
    App
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<div id="app">
  <App></App>
</div>

Upvotes: 2

Related Questions