Load routes from api in vue - vue.js

I'm trying to load routes in a Vue application from my API. I tried pushing data to routes variable and using addRoutes method. But no luck. I think there could be an issue with async. But why the addRoutes() not working?
Here's my code:
import Vue from 'vue';
import VueRouter from 'vue-router';
import axios from 'axios';
/**
* Routes
*/
import item_index from '../../app/Tenant/Item/Views/Index.vue';
import contact_index from '../../app/Tenant/Contact/Views/Index.vue';
import eav_index from '../../app/Tenant/Eav/Views/create.vue';
import eav_create from '../../app/Tenant/Eav/Views/create.vue';
var routes = [
{ path: '/items', component: item_index, name: 'item_index' },
{ path: '/contact', component: eav_index , name: 'contact_index' , props: { entity_type_id: 1 }},
];
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes
});
axios
.get('http://c1.fmt.dev/api/eav/entity_types')
.then(response => {
for (var i = 0; i < response.data.length; i++) {
var type = response.data[i];
var route = {
path: '/' + type.name,
component: eav_index,
name: type.name + '_index',
props: {
entity_type_id: type.id
},
};
router.addRoutes([route]);
alert(router.options.routes);
// alert(JSON.stringify(routes));
}
})
.catch(error => {
console.log(error)
});
new Vue({
el: '.v-app',
data(){
return {
page_header: '',
page_header_small: '',
}
},
router, axios
});

Try this improved code. Without postponing Vue instance creation, so without unnecessary delaying page interactivity:
import Vue from 'vue'
import VueRouter from 'vue-router'
import axios from 'axios'
import item_index from '../../app/Tenant/Item/Views/Index.vue'
import contact_index from '../../app/Tenant/Contact/Views/Index.vue'
import eav_index from '../../app/Tenant/Eav/Views/create.vue'
import eav_create from '../../app/Tenant/Eav/Views/create.vue'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes: [{
path: '/items',
component: item_index,
name: 'item_index'
}, {
path: '/contact',
component: eav_index ,
name: 'contact_index' ,
props: {entity_type_id: 1}
}]
})
new Vue({
el: '.v-app',
router,
data () {
return {
page_header: '',
page_header_small: '',
}
},
methods: {
getDynamicRoutes (url) {
axios
.get(url)
.then(this.processData)
.catch(err => console.log(err))
},
processData: ({data}) => {
data.forEach(this.createAndAppendRoute)
},
createAndAppendRoute: route => {
let newRoute = {
path: `/${route.name}`,
component: eav_index,
name: `${route.name}_index`,
props: {entity_type_id: route.id}
}
this.$router.addRoutes([newRoute])
}
},
created () {
this.getDynamicRoutes('http://c1.fmt.dev/api/eav/entity_types')
}
})
For better code structure and readability, move router definition to separate file:
In your main file, leave just this code:
// main.js
import Vue from 'vue'
import router from '#/router'
import axios from 'axios'
new Vue({
el: '.v-app',
router,
data () {
return {
page_header: '',
page_header_small: '',
}
},
methods: {
getDynamicRoutes (url) {
axios
.get(url)
.then(this.processData)
.catch(err => console.log(err))
},
processData: ({data}) => {
data.forEach(this.createAndAppendRoute)
},
createAndAppendRoute: route => {
let newRoute = {
path: `/${route.name}`,
component: eav_index,
name: `${route.name}_index`,
props: {entity_type_id: route.id}
}
this.$router.addRoutes([newRoute])
}
},
created () {
this.getDynamicRoutes('http://c1.fmt.dev/api/eav/entity_types')
}
})
And in same folder as main file lies, create subfolder 'router' with 'index.js' within:
// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import item_index from '../../../app/Tenant/Item/Views/Index.vue'
import contact_index from '../../../app/Tenant/Contact/Views/Index.vue'
import eav_index from '../../../app/Tenant/Eav/Views/create.vue'
import eav_create from '../../../app/Tenant/Eav/Views/create.vue'
Vue.use(VueRouter)
export default new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes: [{
path: '/items',
component: item_index,
name: 'item_index'
}, {
path: '/contact',
component: eav_index ,
name: 'contact_index' ,
props: {entity_type_id: 1}
}]
})

The vue instance is already initiated when you try to add the routes (same problem as in this question: How to use addroutes method in Vue-router? ). You could postpone the vue initalization after the routes are loaded:
//imports and stuff...
axios
.get('http://c1.fmt.dev/api/eav/entity_types')
.then(response => {
for (var i = 0; i < response.data.length; i++) {
var type = response.data[i];
var route = {
path: '/' + type.name,
component: eav_index,
name: type.name + '_index',
props: {
entity_type_id: type.id
},
};
// extend routes array prior to router init
routes.push(route);
}
// init router when all routes are known
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes
});
// init vuw instance when router is ready
new Vue({
el: '.v-app',
data(){
return {
page_header: '',
page_header_small: '',
}
},
router, axios
});
})
.catch(error => {
console.log(error)
});

Related

VueJs: cannot use router.push

So, i want to vue router.push on my store.js, but i keep getting error Cannot read property 'push' of undefined
i've tried to import vue-router in my store.js, but still in vain
here's my app.js :
import Vue from 'vue'
import VueRouter from 'vue-router'
//Import and install the VueRouter plugin with Vue.use()
Vue.use(VueRouter)
import App from './views/App'
import Home from './views/Home'
import Login from './views/Login.vue'
import Register from './views/Register.vue'
const router = new VueRouter({
mode: 'history',
routes: [{
path: '/',
name: 'home',
component: Home,
meta: { requiresAuth: true }
},
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/register',
name: 'register',
component: Register
},
],
});
const app = new Vue({
el: '#app',
components: { App },
router,
});
and here's my store.js :
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
accessToken: null,
loggingIn: false,
loginError: null
},
mutations: {
loginStart: state => state.loggingIn = true,
loginStop: (state, errorMessage) => {
state.loggingIn = false;
state.loginError = errorMessage;
},
updateAccessToken: (state, accessToken) => {
state.accessToken = accessToken;
},
logout: (state) => {
state.accessToken = null;
}
},
actions: {
doLogin({ commit }, loginData) {
commit('loginStart');
console.log(loginData)
axios.post('http://localhost:8000/api/login', loginData)
.then(response => {
console.log(response)
let accessToken = response.data.jwt;
document.cookie = 'jwt_access_token=' + accessToken;
commit('updateAccessToken', accessToken);
///this caused the error
this.$router.push({ path: '/' })
})
.catch(error => {
// commit('loginStop', error.response.data.error);
console.log(error)
commit('updateAccessToken', null);
console.log(error.response);
})
}
}
})
as you can see, after i call doLogin() function, and using axios, it stop at the this.$router.push({ path: '/' }) line, causing error.
You need to make a router.js file
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const router = new Router({
...
})
export default router
In app.js replace the import of the vue-router to your new router.js and remove Vue.use(Router).
In the store, this is not the Vue instance.
Import the router.js in your store.js;
import router from './router'
Then you can access it like this;
router.push({ path: '/' })
I also noticed that you haven't add the store to the Vue instance. Import and add in app.js.
import store from './store'
...
const app = new Vue({
el: '#app',
components: { App },
router,
store //<<<<<<<<
});

Vue Js Axios get method

I use vue.js and I try to set a parameter id in axios.get request and I can't understand how exactly to do it
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Movie from './views/Movie.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
},
{
path: '/movie/:m_id',
name: 'movie',
component: Movie
}
]
})
import Navbar from '../components/Navbar'
import axios from "axios"
export default {
components:{
Navbar
},
data () {
return {
movi: null,
}
},
mounted () {
axios
.get(`https://api.themoviedb.org/3/movie/${m_id}?api_key=7bc75e1ed95b84e912176b719cdeeff9&language=en-US`)
.then(response => (this.movi= response.data))
}
}
I am trying to pass to axios this id of the page to get information about that specific movie and I got stuck.
Any help?
You can try this to use your params from the URL:
// Retrieve the `m_id` param from the `this.$route.params` object:
this.$route.params.m_id
For more info see https://router.vuejs.org/api/#route-object-properties
#How can I do the same thing but in Vuex
import Vue from 'vue'
import Vuex from 'vuex'
import Axios from 'axios';
import router from './router'
Vue.use(Vuex)
Vue.use(Axios)
Vue.use(router)
export default new Vuex.Store({
// data() {
// return {
// m_id:this.$route.params.m_id
// }
// },
// m_id : this.$route.params.m_id,
state: {
video_key: [],
},
mutations: {
updateInfo (state , video_key){
state.video_key = video_key
}
},
getters:{
m_id : this.route.params.m_id
},
actions: {
fetchData({commit,getters}){
axios.get(`https://api.themoviedb.org/3/movie/${this.m_id}/videos?api_key=7bc75e1ed95b84e912176b719cdeeff9&language=en-US`)
.then(response =>{
commit('updateInfo',response.data.results[0].key)
})
}
}
})

vuejs vue-router: TypeError: Cannot read property 'push' of undefined [duplicate]

This question already has answers here:
How to navigate using vue router from Vuex actions
(6 answers)
Closed 3 years ago.
I am having trouble loading a page with vue-router. It appears that my $router var isn't being reached.
When I console log this.$router I receive an undefined. However, console logging this returns the store object in dev tools.
Here are the relevant scripts:
main.js
import Vue from "vue";
import VueCookies from 'vue-cookies';
import App from "./App.vue";
import router from "./router";
import { store } from "./store/store";
import BootstrapVue from "bootstrap-vue";
import "./registerServiceWorker";
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue/dist/bootstrap-vue.css";
import "../css/bracket.min.css";
Vue.use(BootstrapVue);
Vue.use(VueCookies);
// set default config
VueCookies.config('1d');
// set global cookie
VueCookies.set('theme','default');
VueCookies.set('hover-time','1s');
require("../css/bracket.min.css");
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
router.js
import Vue from "vue";
import Router from "vue-router";
// import Home from "#/views/Home.vue";
import Splash from "#/components/Splash.vue";
import Dash from "#/components/Dash.vue";
import Signup from "#/views/Signup.vue";
import finalSignup from "#/components/finalSignup.vue";
import providerDash from "#/views/ProviderDash.vue";
import employeeDash from "#/views/EmployeeDash.vue";
import Login from "#/views/Login.vue";
Vue.use(Router);
export default new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "home",
component: Splash
},
{
path: "/login",
name: "login",
component: Login
},
{
path: "/signup",
name: "signup",
component: Signup
},
{
path: "/provider-full-name",
name: "finalSignup",
component: finalSignup
},
{
path: "/provider-dashboard",
name: "providerDash",
component: providerDash
},
{
path: "/employee-dashboard",
name: "employeeDash",
component: employeeDash
},
{
path: "/about",
name: "about",
component: () =>
import(/* webpackChunkName: "about" */ "./views/About.vue")
}
]
});
userSession.js (vuex module)
-The action in question is named authenticateUserSession
import Axios from "axios";
const userSession = {
namespaced: true,
state: {
email: '',
password: ''
},
mutations: {
SET_EMAIL: (state, payload) => {
state.email = payload;
},
SET_PASSWORD: (state, payload) => {
state.password = payload;
}
},
actions: {
setEmail(context, email) {
context.commit('SET_EMAIL', email)
},
setPassword(context, password) {
context.commit('SET_PASSWORD', password)
},
authenticateUserSession(context, {email, password}) {
context.dispatch('setEmail', email);
context.dispatch('setPassword', password);
Axios.post('http://localhost:3000/api/v1/sessions', {}, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'user-email': context.state.email,
'user-password': context.state.password
}
})
.then((response) => {
// console.log(response.data.locals.token);
// console.log(this.$router);
// console.log(this);
let jwt = response.data.locals.token
window.$cookies.set('jwt', jwt);
this.$router.push("home");
})
.catch(function(error) {
console.log(error);
})
}
},
getters: {
getEmail: (state) => {
return state.email;
},
getPassword: (state) => {
return state.password;
}
}
}
export default userSession;
Why am I missing access to the vue-router variable ($router/this.$router) & unable to render a specified route?
The content in #yuriy636's link in addition to the other links in the post helped me resolve!
I ended up doing the following:
import router from "../../router";
.
.
.
// inside the authenticateUserSession action, in axios response
router.push("home")";
Many thanks #yuriy636!

VueJS dynamic routes and components

Using cue-cli 3. Is it possible to do this (router.js):
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: e.template,
},
]);
});
});
e.template is a string 'Default' and of course VueJS says:
route config "component" for path: /privacy-policy cannot be a string id. Use an actual component instead. Tried with Vue.component(e.template) no luck.
What I want to do here is create dynamic routes based on XHR response.
Here is all router.js code:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Default from './views/Default.vue'
import Test from './views/Test.vue'
import axios from "axios";
Vue.use(Router);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
]
});
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: e.template,
},
]);
});
});
export default router;
Currently I ended up with this solution:
function getComponent(name) {
let component = null;
switch(name)
{
case 'Default':
component = Default;
break;
case 'Test':
component = Test;
break;
}
return component;
}
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: getComponent(e.template),
},
]);
});
});
Another one more cleaner solution:
const components = { Default, Test }
axios.get( `${process.env.VUE_APP_API_DOMAIN}/wp-json/api/v1/routes`).then( r => r.data ).then(routes => {
routes.pages.forEach( (e) => {
router.addRoutes([
{
path: `/${e.slug}`,
component: components[e.template],
},
]);
});
});
If e.template stores the template string,
You should wrap it as one options object like {template: e.template, props: {}, data: function () {} }, then call Vue.extend to construct the component.
or you can ignore Vue.extend because Vue will call Vue.extend to construct the component automatically.
Check the usage at Vue Guide: Vue.component
Edit as the OP states e.tempate is one component name:
if e.template is the name of component, uses Vue.component(e.template).
Vue.config.productionTip = false
const router = new VueRouter({
routes: [
]
})
Vue.component('test', {
template: '<div>I am Predefined component -> {{index}}</div>',
props: ['index']
})
let routerIndex = 1
setInterval(()=> {
let newComponent = routerIndex%2 ? {template: '<div>I am User -> {{index}}</div>', props: ['index']} : Vue.component('test')
router.addRoutes([{
path: '/Test' + routerIndex,
name: 'Test' + routerIndex,
component: newComponent,
props: { index: routerIndex }
}])
console.log('add route = ', '/Test' + routerIndex, ' by ', routerIndex%2 ? 'options object' : 'Vue.component')
routerIndex++
}, 2000)
Vue.use(VueRouter)
app = new Vue({
el: "#app",
router,
data: {
routeIndex: 0
},
watch: {
routeIndex: function (newVal) {
this.$router.push({'name': 'Test'+newVal})
}
}
})
div.as-console-wrapper {
height: 100px;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>Current Route Index: {{routeIndex}}</p>
Test Route: <input v-model="routeIndex" type="number">
<router-view></router-view>
</div>

VueRouter's beforeEach causes error: failed to convert exception to string

My console says:
[vue-router] uncaught error during route navigation:
<failed to convert exception to string>
The line that causes that error is the one in main.js:
next('/login')
my main.js file:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import App from './App.vue'
import Routes from './routes'
import { store } from './store/store';
Vue.use(VueRouter);
const router = new VueRouter({
routes: Routes
})
router.beforeEach((to, from, next) => {
next('/login');
})
var vm = new Vue({
el: '#app',
store: store,
router: router,
render: h => h(App),
})
My routes.js file:
export default [
{ path: '/', component: Game, cors: true },
{ path: '/login', component: Login },
{ path: '/signin', component: SignIn },
{ path: '/gamerouter', component: GameRouter },
{ path: '/waitingforplayers', component: WaitingForPlayers, name: "buba" },
{ path: '/selectPlayersForTeams', component: SelectPlayersForTeams },
{ path: '/startStatistics', component: StartStatistics },
{ path: '/gamelocked', component: GameLocked },
{ path: '/answering', component: Answering },
]
I also get this error if i put next('/'), but i do not get this error if i write next() or next(false);
Any ideas that might help me fix this?
next has to be called without parameters to confirm navigation, otherwise you will keep triggering the beforeEach hook:
router.beforeEach(function(to, from, next) {
console.log(to.path)
if (to.path !== '/timer') {
next("/timer");
} else {
next();
}
});