6

How do I create the routes array dynamically, after fetching it via ajax?

Is there a way to add/push new routes to the router after it has been initialized?

This doesn't work:

new Vue({
  el: '#app',
  template: '<App/>',
  data: {
    content: []
  },
  created: function () {
    this.$http.get('dummyjsondatafornow').then((response) => {

      // this doesn't work when creating the VueRouter() outside the Vue instance, as in the docs.
      // this.$router.options.routes.push({ path: '/about', component: About })

      let routes = [
        { path: '/about', component: About }
      ]

      // this doesn't work either
      this.router = new VueRouter({
        routes: routes
      })
    })
  },
  // router: router,
  components: { App }
})
Emin
  • 361
  • 8
  • 18
  • I can understand creating dynamic routes for server side, but dynamic routes for SPA? Where's the point? – Marek Urbanowicz Nov 17 '16 at 16:28
  • Well, the content, including pages/routes for the SPA are managed in a separate system (CMS) from the SPA. How else could I solve this? – Emin Nov 17 '16 at 17:16

3 Answers3

6

I don't believe there is no.

That said you can wildcard the route so that may provide you with an alternative.

I built a site where the backend (and in turn pages created) were controlled via a CMS which served all pages to Vue as JSON. This meant Vue wasn't aware of the routes the backend was creating.

Instead we passed all the CMS pages to Vue Router via a single * wildcard component. In Vue Router 2 this would look like:

const routes = [
  { path: '*', component: AllPages }
]

Vue Router 2 allows for Advanced Matching Patterns

These allow you to set a wide variety of conditions, therefore whilst you can't inject the object passed back via ajax into your router you can add a dynamic component to an AllPages component that is wildcard matched. This would allow you to pass the name of the component to load via your ajax request and then load that component when the page is called. i.e.

Your Ajax response:

{
  // url: component name
  '/about/': 'about',
  '/about/contact/': 'contact',
  ...
}

Then in an AllPages vue component:

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

<script>

  module.exports = {
    data () {
      return {
        currentView: '',
        ajaxRoutes: {}, // loaded via ajax GET request
        ...
      }
    },
    // watch $route to detect page requests
    watch: {
      '$route' (to, from) {
        if (this.ajaxRoutes[to]) {
          this.currentView = this.ajaxRoutes[to]
        }
      }
    },
    ...
  }

</script>

The above is a rather abbreviated idea but essentially you dynamically load the component based on the path the user requested.

GuyC
  • 6,494
  • 1
  • 31
  • 44
4

I think this is fixed in version 2.3.0. You can now run

router.addRoutes(routes);

to dynamically add routes.

https://github.com/vuejs/vue-router/commit/0e0fac91ab9809254174d95c14592e8dc2e84d33

billaraw
  • 938
  • 1
  • 7
  • 28
0

I have the same situation wherein my routes are built on the backend as it is maintained thru a CMS. With that, I was able to retrieve my routes thru an API call then return it on the vue router. Here's my take:

routes.js

const router = store.dispatch('cms/fetchRoutes').then((response) => {
    const router = new VueRouter({
        routes: response,
        mode: 'history',
        ...
    });
    ...
    return router;
});

export default router;

main.js

import router from './router';
....
router.then((router) => {
    const app = new Vue({
        router,
        store,
        render: (h) => h(App),
    }).$mount('#app')
    ...
});

Basically I do an axios call to fetch my routes then inject the response to the VueRouter routes property. Then on the main.js, do another then and inject the return on the Vue.

By then, my menus are now being retrieved from the database. No more hard coded paths.

basagabi
  • 4,900
  • 6
  • 38
  • 84