Hooking into Vue Router from NuxtJS
The NuxtJS application framework for Vue.js replaces a lot of the low-level configuration through conventions, e.g. routing. But what if you need access to that configuration to implement a certain feature? For example, the vuex-router-sync module watches for route changes to sync the current route with the Vuex state. How could this be done in NuxtJS?
In NuxtJS, plugins provide a way to run custom code before the application is initialized. That's the right place for setting up global hooks. I didn't find it immediately obvious from the documentation, but the plugin receives the NuxtJS context as its parameter.
I must admit that even after figuring this out, I was still wondering how to get access to the router instance until I found an example on GitHub. The Vue router module makes itself available as a property of the Vue instance which is exposed as the app
property of the NuxtJS context. This has allowed me to come up with the following simplified implementation of the vuex-router-sync module:
import { Context } from '@nuxt/types';
import { RootState } from '~/store';
export default ({ app: { store, router } }: Context) => {
if (!(router && store)) {
return;
}
router.afterEach((to, _from) => {
store.commit('setFilter', to.query['filter']);
});
store.watch(
(state: RootState) => state.filter,
(filter: string) => {
router.push({ query: { filter } });
}
);
};
The plugin hooks into the router and the store to synchronize a single value between the URL query string and the Vuex state. To enable it, just register it in nuxt.config.js
:
export default {
// ...
plugins: [{ src: '~plugins/queryParamSync.ts' }],
// ...
};
Of course, the code relies on the store configuration. Below is the relevant part, following the NuxtJS convention:
export interface RootState {
filter: string;
}
export const state: () => RootState = () => ({
filter: '',
});
export const mutations = {
setFilter(state: RootState, value: string) {
state.filter = value || '';
},
};
As a page modifies the state, the URL query string will update automatically. The following computed property and method can take care of that:
import Vue from 'vue';
export default Vue.extend({
computed: {
filter() {
return this.$store.state.filter;
},
},
methods: {
updateFilter(event: InputEvent) {
this.$store.commit('setFilter', (event.target as HTMLInputElement).value);
},
},
});
Here's how they could be hooked up to the template, for example:
<div>
<label for="filter">Filter</label>
<input id="filter" type="text" :value="filter" @input="updateFilter" />
</div>
Full code for a working sample application is available in my GitHub repository.
In NuxtJS, plugins can be created to add custom application bootstrap code. The context parameter gives access to all NuxtJS internals, including its Vue instance. This makes it simple to integrate Vue.js modules and JavaScript libraries in general even if there's no NuxtJS plugin to download for that.