addRoute doesn't update route list - vue.js

I try to adding routes to my router while the application is already running, more specifically after the connection.
I have this in my app.js
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{
path : '/',
name: 'principal',
component : PrincipalComponent,
},
{
path : '/403',
component : Erreur403Component,
}
];
const router = new VueRouter({routes});
export {router}
And in my vuex
import {router} from "../app.js"
const actions = {
loginUser({state, commit}, user) {
return new Promise((resolve, reject) => {
axios.get('/menuGeneration')
.then(data => {
data.data.forEach(route => {
router.addRoute({
path : "\test"
});
router.push("\test");
})
})
.catch(err => console.log(error);
console.log(router.getRoutes());
})
}
}
But I only have the first 2 routes declared in app.js
I have checked that axios sends me the list of menus
If anyone has a lead, I'm intereste !
For information :
vue#2.6.12
vue-router#3.6.5
vuex#3.6.0
UPDATE
After the advice yoduh of I modified the code following
axios.get('/menuGeneration')
.then(data => {
data.data.forEach(route => {
router.addRoute({
path : "/test",
component : TestComponent
});
})
console.log(router.getRoutes());
})
.catch(err => console.log(error);
And it's work
But when i add a replaces for the current resource
axios.get('/menuGeneration')
.then(data => {
data.data.forEach(route => {
router.addRoute({
path : "/test",
component : TestComponent
});
})
window.location.replace('/dashboard')
})
.catch(err => console.log(error);
it does not guard the routes.
what is the way to keep the routes ?

it does not guard the routes.
what is the way to keep the routes ?
You must always navigate using vue-router if you want vue-router's navigation guards to activate on route changes. This line:
window.location.replace('/dashboard')
can be replaced with the vue-router replace equivalent which will activate your navigation guards
router.replace('/dashboard')

Related

Vue SSR issue with no ssr

I'm using my own SSR boilerplate for Vue.
https://github.com/Djancyp/luna-vue-ssr-boilerplate
All working fine pages are SSR rendering.
The issue that I'm currently having is no SSR components are failing as soon as I import them to components.
server trow err:
ReferenceError: document is not defined
I'm aware of the issue is document does not exist on nodejs server.
My question is How can I stop rendering no SSR components on the server ?
-I've tried No-SSR but no joy.
my server-enty.ts
import createApp from './app'
const config = require('config')
const isDev = process.env.NODE_ENV !== 'production'
export default context => {
return new Promise((resolve, reject) => {
console.log('what the f server')
const s = isDev && Date.now()
const { app, router, store } = createApp(config)
const { url } = context
const { fullPath } = router.resolve(url).route
if (fullPath !== url) {
return reject({ url: fullPath })
}
router.push(url)
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
console.log(matchedComponents)
if (!matchedComponents.length) {
console.log('what the **** mate error')
return reject({ code: 404 })
}
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
const meta = (app as any).$meta()
context.meta = meta
context.state = store.state
resolve(app)
}).catch(err => {
console.log(err)
return reject
})
}, reject)
})
}
Vue-SSR executes asyncData and serverPrefetch on the server-side. Remove these from your component, move the code to created/ 'mounted' and it should stop rendering on the server-side.
My assumption is that you are executing document. within asyncData or serverPrefetch.
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
const meta = (app as any).$meta()
context.meta = meta
context.state = store.state
resolve(app)
}).catch(err => {
console.log(err)
return reject
})
}, reject)
the issue was that I was rejecting if the async template not exist.
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
const meta = (app as any).$meta()
context.meta = meta
context.state = store.state
resolve(app)
}).catch(err => {
console.log(err)
return reject
})
})
this resolved the issue but needs to improve the code a little bit.
Thanks for helps guys.

Mocking axios in Jest returns axios is not defined

I have seen similar questions but they dont actually address what am looking for.
so am using using axios globally in app.js for my vue app like window.axios=require('axios')
then in auth.js i have this
export function login(credentials){
return new Promise((res,rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
res(response.data);
})
.catch((err) => {
rej("Wrong email or password");
})
});
}
which works fine on the login page
however in my test script
jest.mock("axios", () => ({
post: jest.fn(() => Promise.resolve({data:{first_name:'James','last_name':'Nwachukwu','token':'abc123'}}))
}));
import axios from 'axios'
import {login} from '../helpers/auth'
it("it logs in when data is passed", async () => {
const email='super#gmail.com'
const password='secret';
const result=await login({email,password});
expect(axios.post).toBeCalledWith('/api/auth/login',{"email": "super#gmail.com", "password": "secret"})
expect(result).toEqual({first_name:'James','last_name':'Nwachukwu','token':'abc123'})
})
shows axios is not defined
but if i change auth.js to
import axios from 'axios'
export function login(credentials){
return new Promise((res,rej) => {
axios.post('/api/auth/login', credentials)
.then((response) => {
res(response.data);
})
.catch((err) => {
rej("Wrong email or password");
})
});
}
test passes. how do i run the test without having to import axios on each vue file
I had the same problem just now. I am also including axios via window.axios = require('axios'); in my app.js.
The solution is to set your axios mock on window.axios in your test. So instead of this (incorrect):
axios = {
post: jest.fn().mockName('axiosPost')
}
const wrapper = mount(Component, {
mocks: {
axios: axios
}
})
When your component code calls axios.whatever it is really calling window.axios.whatever (as I understand it), so you need to mirror that in your test environment:
window.axios = {
post: jest.fn().mockName('axiosPost')
}
const wrapper = mount(Component, {
mocks: {
axios: window.axios
}
})
And in your test:
expect(window.axios.post).toHaveBeenCalled()
The above method works fine until you want to chain then to it. In which case you need to set your mock up like this:
window.axios = {
get: jest.fn(() => {
return {
then: jest.fn(() => 'your faked response')
}
}),
}
You don't need to pass it into the component mock though, you can just mount (or shallowMount) the component as usual

How to do final state injection with Vue SRR with V8Js

The Vue SSR guide is mainly written for running a nodejs server and just touches on using V8Js in the final chapter.
It does have a section on final state injection but this doesn't work in the context of V8Js.
How can we pass the Vuex state from server to client side when using V8Js?
First in entry-server.js we need to print not just the app, but also the Vuex state.
import { createApp } from './app'
new Promise((resolve, reject) => {
const { app, router, store } = createApp()
router.push(url)
// Wait until router has resolved possible async components and hooks.
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// No matched routes, reject with 404.
if (matchedComponents.length === 0) {
return reject({ code: 404 })
}
resolve(app)
}, reject)
})
.then(app => {
renderVueComponentToString(app, (err, res) => {
// Only now the app has rendered and all components have had a chance to
// populate the Vuex store can we set the initial state.
const initialState = JSON.stringify(app.$store.state)
print(`<script>window.__INITIAL_STATE__ = ${initialState}</script>\n\r`)
// Print the markup for the app.
print(res)
})
})
.catch((err) => {
print(err)
})
And then in entry-client.js we need to populate the Vuex store with that data:
import { createApp } from './app'
const { app, router, store } = createApp()
if (window.__INITIAL_STATE__) {
// Initialize the store state with the data injected from the server.
store.replaceState(window.__INITIAL_STATE__)
}
router.onReady(() => {
app.$mount('#app')
})

passing data to route's component beforeEnter

I have the following VueRouter route
{
path: '/playlists/:id',
name: 'videos',
component: Video,
props: {
videos: [],
},
beforeEnter(to, from, next) {
Vue.axios
.get('/playlistitems?playlistId='.concat(to.params.id))
.then((response) => {
to.params.videos = response.data
next((vm) => {
console.log(vm)
vm.videos = response.data
})
})
.catch((err) => console.log('error', err))
},
}
When the route is entered into everything executes as expected but I'm not sure how to pass the response.data to the route's component Videos
Question 1
Can you set the Vue component's props property from the Router?
Question 2
The route is a dynamic route. If the route and component is already loaded and the dynamic parameter changes....does beforeEnter still fire? If not where should I put my data fetching logic? Do I watch for route changes inside the Vue component?
1)
This might not be the most elegant approach, but here's a way to achieve that:
let videos = [];
export default new Router({ ... });
{
path: '/playlists/:id',
name: 'videos',
component: Video,
props: route => ({ videos }),
beforeEnter (to, from, next) {
Vue.axios.get('/playlistitems?playlistId='.concat(to.params.id))
.then((response) => {
// Now the data will be available when the props will be initialized
videos = response.data
next()
})
.catch((err) => console.log('error', err))
}
}
// Videos.vue
props: {
videos: Array
},
2)
IMHO, it would be easier if you could encapsulate the logic in the component.
What do I mean by that is that you could fetch your data in the created hook and set it to a variable that you defined in your data function.
data() {
return {
videos: []
}
},
created () {
Vue.axios.get('/playlistitems?playlistId='.concat(this.$route.params.id))
.then((response) => {
this.videos = response.data;
})
.catch((err) => console.log('error', err))
},
Another approach, which may be suitable or not, depending on what you're working on, would be to have a parent component that fetches multiple playlists which could be stored in vuex.
Thus, you would have another component that handles playlists/:id.
Within this last mentioned component, in your created hook, you would have something like this:
created () {
this.$store.commit('playlist/SET_CURRENT_PLAYLIST_ID', this.$route.params.id);
this.videos = this.$store.getters['playlits/getVideosByPlaylistId'];
},

How to setup vuex and vue-router to redirect when a store value is not set?

I'm working with the latest versions of vue-router, vuex and feathers-vuex and I have a problem with my router.
What I'm doing is to check if a route has the property "requiresAuth": true in the meta.json file. If it does then check the value of store.state.auth.user provided by feathers-vuex, if this value is not set then redirect to login.
This works fine except when I'm logged in and if I reload my protected page called /private then it gets redirected to login so it seems that the value of store.state.auth.user is not ready inside router.beforeEach.
So how can I set up my router in order to get the value of the store at the right moment?
My files are as follow:
index.js
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'
const meta = require('./meta.json')
// Route helper function for lazy loading
function route (path, view) {
return {
path: path,
meta: meta[path],
component: () => import(`../components/${view}`)
}
}
Vue.use(Router)
export function createRouter () {
const router = new Router({
mode: 'history',
scrollBehavior: () => ({ y: 0 }),
routes: [
route('/login', 'Login')
route('/private', 'Private'),
{ path: '*', redirect: '/' }
]
})
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
if (!store.state.auth.user) {
next('/login')
} else {
next()
}
} else {
next()
}
})
return router
}
meta.json
{
"/private": {
"requiresAuth": true
}
}
I fixed the issue by returning a promise from vuex action and then run the validations
router.beforeEach((to, from, next) => {
store.dispatch('auth/authenticate').then(response => {
next()
}).catch(error => {
if (!error.message.includes('Could not find stored JWT')) {
console.log('Authentication error', error)
}
(to.meta.requiresAuth) ? next('/inicio-sesion') : next()
})
})