How to load all server side data on initial vue.js / vue-router load?
My approach is to delay construction of the store and main Vue until my AJAX call has returned.
store.js
import Vue from 'vue';import Vuex from 'vuex';import actions from './actions';import getters from './getters';import mutations from './mutations';Vue.use(Vuex);function builder(data) { return new Vuex.Store({ state: { exams: data, }, actions, getters, mutations, });}export default builder;
main.js
import Vue from 'vue';import VueResource from 'vue-resource';import App from './App';import router from './router';import store from './store';Vue.config.productionTip = false;Vue.use(VueResource);Vue.http.options.root = 'https://miguelmartinez.com/api/';Vue.http.get('data') .then(response => response.json()) .then((data) => { /* eslint-disable no-new */ new Vue({ el: '#app', router, store: store(data), template: '<App/>', components: { App }, }); });
I have used this approach with other frameworks such as Angular and ExtJS.
You can use navigation guards.
On a specific component, it would look like this:
export default { beforeRouteEnter (to, from, next) { // my ajax call }};
You can also add a navigation guard to all components:
router.beforeEach((to, from, next) => { // my ajax call});
One thing to remember is that navigation guards are async, so you need to call the next()
callback when the data loading is finished. A real example from my app (where the guard function resides in a separate file):
export default function(to, from, next) { Promise.all([ IngredientTypes.init(), Units.init(), MashTypes.init() ]).then(() => { next(); });};
In your case, you'd need to call next()
in the success
callback, of course.
Alright, I finally figured this thing out. All I'm doing is calling a synchronous ajax request within my main.js
file where my root vue instance is instantiated, and assigning a data property the requested data as so:
main.js
let acfData;$.ajax({ async: false, url: 'http://localhost/placeholder/wp-json/acf/v2/page/2', type: 'GET', success: function(response) { console.log(response.acf); acfData = response.acf; }.bind(this)}) const router = new VueRouter({ routes: [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/tickets', component: Tickets }, { path: '/sponsors', component: Sponsors }, ], hashbang: false});exports.router = router;const app = new Vue({ router, data: { acfs: acfData }, created() { }}).$mount('#app')
From here, I can use the pulled data within each individual .vue
file / component like so:
export default { name: 'app', data () { return { acf: this.$parent.acfs, }},
Finally, I render the data within the same .vue
template with the following:
<template> <transition name="home" v-on:enter="enter" v-on:leave="leave" v-bind:css="false" mode="out-in" > <div class="full-height-container background-image home" v-bind:style="{backgroundImage: 'url(' + this.acf.home_background_image.url + ')'}"> <div class="content-container"> <h1 class="white bold home-title">{{ acf.home_title }}</h1> <h2 class="white home-subtitle">{{ acf.home_subtitle }}</h2> <div class="button-block"> <a href="#/about"><button class="white home-button-1">{{ acf.link_title_1 }}</button></a> <a href="#/tickets"><button class="white home-button-2">{{ acf.link_title_2 }}</button></a> </div> </div> </div> </transition></template>
The most important piece of information to take away, is that all of the ACF data is only being called ONCE at the very beginning, compared to every time a route is visited using something like beforeRouteEnter (to, from, next)
. As a result, I'm able to get silky smooth page transitions as desired.
Hope this helps whoever comes across the same problem.