Component Not Loading with VueRouter Locally. Works in Production - vuejs2

Sorry if this an easy question, but I temporarily inherited an app written in node.js / express and a Vue 2.5.2 frontend. I cannot get a specific route to work locally in my test environment that works in the live environment.
Say we've emailed a user a link. When they click on it, it's supposed to open this specific Vue component. But it just loads the home page; 200 status.
Here's the relevant part in the node/express SPA:
app.get('/', (req, res) => {
return res.render('index.ejs', { has_token: false });
});
app.get('/token/:token', (req, res) => {
return res.render('index.ejs', { has_token: true });
});
It sets the has_token variable. Here is the relevant Vue part in the SPA:
new Vue({
el: '#app',
components: {
LandingPage,
Register,
Home
},
router,
store,
created() {
if(has_token) {
console.log('has token'); # WE MAKE IT HERE
this.$router.push('/TokenRegistration');
}
...
...
Here is a snippet of the routes created
const routes = [
{
name: '/',
path: '/',
component: LandingPage
},
{
name: 'Register',
path: '/Register',
component: Register
},
{
name: 'TokenRegistration',
path: '/TokenRegistration',
component: TokenRegistration
},
And them being loaded
const router = new VueRouter({
routes
});
Sorry if this is an easy question, but I haven't used node, express or VueRouter before, and this is the only thing stumping me. I double checked and made sure both environments have their repos in sync and rebuilt the app and reboot the node service just in case.
Summary:
In production, I go to .com/token/<some string> it loads the specified component TokenRegistration.
In local dev, I go to .com/token/<some string> it just loads the home page LandingPage Component
And there are no JS errors; page load 200 success.

Related

Process 404 page when there is no parameter in Vue

Dynamic routing is in use.
If there is no device data in vuex, I want to go to 404 page.
How should I implement it?
router/index.js
const routes = [
{
path: '/',
name: 'Main',
component: Main
},
{
path: '/:device',
name: 'Detail',
component: Detail,
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound
},
]
When the device-detail page is implemented as follows, it does not move to the 404 page.
const deviceName = route.params.device
const storedDeviceList = computed(() => store.state.stationName)
if (!storedDeviceList.value.includes(deviceName)) {
router.push({
name: 'NotFound'
})
}
I think the first problem is, that you declare router two times in your project, according to your github repo. You declared your routes in your router/index.js and imported it into your main.js. So importing it again in About.vue from vue-router instead of router.js causes, that this instance has no routes. The second problem is the same with your store, as you import store/index.js to your main.js but import a new instance from vuex to your About.vue.
If you would use the composition API, you could call the already in main.js imported modules with this, like:
this.$router.push({
name: 'NotFound'
})
You also would get your states from your store like this:
this.$store.state.stationName
So, in composition API, use something like this in your About.vue:
<script>
export default {
methods: {
checkDevice() {
if (!this.$store.state.deviceList.includes(this.$route.params.device)) {
this.$router.push({
name: 'NotFound'
})
}
}
},
created() {
this.checkDevice()
}
}
</script>

Vue Router, refresh shows blank page

Im trying to figure out why Vue.js routing, after I refresh my admin page re-directs back to the home component, only showing a blank page, and after a second refresh shows the home component again. I am still logged in as I can still go directly with the url to my admin-page. Meaning the session is still active. Is there a way to force the page to stay on the admin home page when I press F5? I tried things like history mode etc, but cant figure it out.
This is my router.js layout in the root and alos my main router file
import Vue from 'vue'
import Router from 'vue-router'
import firebase from "firebase/app";
import Home from './views/user/Home.vue'
import adminRoute from "./components/routes/admin-routes";
Vue.use(Router);
const router = new Router({
routes: [
{
path: '*',
redirect: '/',
},
{
path: '/',
name: 'home',
component: Home,
meta: {
requiresAuth: false,
}
},
...adminRoute
],
});
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.requiresAuth === false)) {
next()
} else if (!firebase.auth().currentUser) {
next(false)
} else {
next()
}
});
export default router
and I have my admin-routes.js
import AdminHome from "../../views/admin/AdminHome";
import Users from "../../views/admin/Users";
const adminRoute = [
{
path: '/admin-home',
name: 'AdminHome',
component: AdminHome,
meta: {
requiresAuth: true
},
},
{
path: '/users',
name: 'Users',
component: Users,
meta: {
requiresAuth: true,
}
}
];
export default adminRoute;
I do want to mention that my main page is under views/user/Home.vue and my AdminHome page is views/admin/AdminHome.vue
This problem is return to the server not the built app
you have to manage your 404 not found root
Here you can find the solution
and if you work With Netlify you just create a file in the root of the project ( same place as package.json) name netlify.toml containing
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
in vue4 they did away with the 'next' argument(see here), this is working for me to send people back to the login page, where I'm using firebaseui, I have a simple boolean flag in my vuex store named "isAuthenticated" that I flip on when a user signs on.
NOTE: the following is a boot file for quasar v2 but the logic will
work where ever you have access to the store
import { computed } from 'vue'
export default ({ app, router, store }) => {
console.log('nav.js:')
const isAuthenticated = computed(() => store.state.auth.isAuthenticated)
console.log('isAuthenticated',isAuthenticated.value)
router.beforeEach((to, from) => {
console.log('nav.js:beforeEach:to',to)
console.log('nav.js:beforeEach:from',from)
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated.value) {
console.log('nav.js:beforeEach:to.redirect')
return '/login'
}
}
})
}

Vue Router not working once production is built

My Vue project works correctly when I build it using dev. However, once I run npm run build and move the files in dist to my webserver, Vue Router doesn't seem to work anymore.
I've tried removing history mode, but this didn't work.
Vue Router
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.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
},
{
path: '/donate',
name: 'donate',
component: () => import('./views/Donate.vue')
},
{
path: '/guildselector',
name: 'guildselector',
meta: {
requiresAuth: true
},
component: () => import('./views/Guildselector.vue')
},
{
path: '/guild/:id',
name: 'guild',
meta: {
requiresAuth: true,
requiresAdmin: true
},
component: () => import('./views/Guildpanel.vue')
}
]
})
export default router
MyWebsite.com/guildselector should show the Guildselector component for example, however I get a
Not Found The requested URL /guildselector was not found on this server.
Only the donate page and landing page work.
This is a very common problem; please read HTML5 History Mode in detail about how to configure your web server to host the files correctly.
Here comes a problem, though: Since our app is a single page client side app, without a proper server configuration, the users will get a 404 error if they access http://oursite.com/user/id directly in their browser.
Simple solution is just comment this line:
mode: 'history',

Vue Router: Keep query parameter and use same view for children

I'm rewriting an existing Angular 1 application with Vue.
The application always needs to authenticate an user by locale, id and token before entering any views. Respecting the conventions of our API, I specified the token as a query parameter within my main parent route.
Coming from the existing Angular's UI router implementation I thought this is the way to go:
// main.js
new Vue({
el: '#app',
router,
store,
template: '<router-view name="main"></router-view>'
})
// router.js
const router = new Router({
mode: 'history',
routes: [
{
name: 'start',
path : '/:locale/:id', // /:locale/:id?token didn't work
query: {
token: null
},
beforeEnter (to, from, next) {
// 1. Get data from API via locale, id and token
// 2. Update store with user data
},
components: {
main: startComponent
},
children: [{
name: 'profile',
path: 'profile',
components: {
main: profileComponent
}
}]
}
]
})
When I navigate to the profile view, I expect the view to change and the query token to stay, e.g. /en-US/123?token=abc to /en-US/123/profile?token=abc. Neither happens.
I'm using Vue 2.3.3 and Vue Router 2.3.1.
Questions:
Can I keep query parameters when navigating to child routes?
Am I using the Vue router right here? Or do I need to blame my UI router bias?
You can resolve this in the global hooks of Router
import VueRouter from 'vue-router';
import routes from './routes';
const Router = new VueRouter({
mode: 'history',
routes
});
function hasQueryParams(route) {
return !!Object.keys(route.query).length
}
Router.beforeEach((to, from, next) => {
if(!hasQueryParams(to) && hasQueryParams(from)){
next({name: to.name, query: from.query});
} else {
next()
}
})
If the new route (to) does not have its own parameters, then they will be taken from the previous route (from)
You can add in a mounted hook a router navigation guard beforeEach like this preserveQueryParams:
// helpers.js
import isEmpty from 'lodash/isEmpty';
const preserveQueryParams = (to, from, next) => {
const usePreviousQueryParams = isEmpty(to.query) && !isEmpty(from.query);
if (usePreviousQueryParams) {
next({ ...to, query: from.query });
} else {
next();
}
};
// StartComponent.vue
removeBeforeEachRouteGuard: Function;
mounted() {
this.removeBeforeEachRouteGuard = this.$router.beforeEach(preserveQueryParams);
}
// don't forget to remove created guard
destroyed() {
this.removeBeforeEachRouteGuard();
// resetting query can be useful too
this.$router.push({ query: undefined });
}

vue-router 2, how to fetch routes via ajax?

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 }
})
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.
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
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.