Search code examples
apivue.jsvue-routerdefault

Vue router: how to pass data to default/ initial route


I have a vue router config set up like so:

[
    {
        name: 'home',
        path: '/',
        component: HomeComponent,
        props: true,
    },

    {
        name: 'detail',
        path: '/detail/:id',
        component: DetailComponent,
        props: true,
    }
]

The app containing this config performs the followin:

  1. Queries an API for an object Foo
  2. Displays a header with some info from Foo
  3. Renders the default ("home") route with more info from Foo and a list of Bar items (contained in Foo) the user can click on to visit the detail route.

The problem is how can I pass the Foo data into the home route on first load? Since the API is part of the app component (as this.api - not a global) I can't hard code home's props inside the router config; and since I can't refer to this inside beforeRouteEnter, I can't use this.api to perform the request before routing to home.

I've tried:

  1. Passing the Foo via <router-view :foo="myFoo" /> - this had no effect whatsoever.
  2. Re-routing to home via this.$router.push() or this.$router.replace() once Foo is loaded. This likewise had no effect, even with $forceUpdate.

I've considered:

  1. Passing in data via next() inside the app's beforeRouteLeave() navigation guard. This strikes me as brittle at best since I will have to account for each specific route that gets a foo prop.
  2. Any number of horrible hacks involving secret hidden routes and other magic.

I also suspect I will face the same issue with any deep linking, since the app will have to also obtain a foo before handing it off to the per-route component.

So. How can I pass data from the top-level app via a prop to any given route at page load time?


Solution

  • It's not clear how exactly you are getting the Foo object, but assuming you are getting it inside of a mounted() hook in App.vue, you could send it to the Home component via Vuex store:

    Method 1

    App.vue

    mounted () {
      this.foo = getFoo()
    },
    computed: {
      foo: {
        get () {
          return this.$store.state.foo
        },
        set (value) {
          // commit or dispatch the object to store
        }
      }
    }
    

    Home.vue

    <div>{{foo}}</div>
    
    computed: {
      foo () {
        return this.$store.state.foo
      }
    }
    

    Method 2 - using an eventBus

    Create a file called eventBus.js:

    eventBus.js

    import Vue from 'vue'
    export const eventBus = new Vue()
    

    Import created eventBus in your main.js where you import Vue and add it to Vue prototype if you want to make it globaly available:

    Main.js

    import { eventBus } from './eventBus'
    
    Vue.prototype.$eventBus = eventBus
    

    App.vue

    mounted () {
      this.foo = getFoo()
      // Send foo via event bus
      this.$eventBus.$emit('transferFoo', this.foo)
    }
    

    Home.vue

    <div>{{foo}}</div>
    
    data () {
      return {
        foo: {}
      }
    },
    mounted () {
      this.$eventBus.$on('transferFoo', data => {
        this.foo = data
      })
    }