VueJS add external site to router history - vue.js

I am building an app using Vue Router that uses an "external" site for login credentials. It essentially looks like this:
Home.Vue
<template>
</template>
<script>
export default {
name: "Home",
data() {
return {
}
},
created() {
window.location = 'https://third_party_site.com?nextpage=http://my_site.com/#/entry'
}
}
</script>
<style scoped>
</style>
This third_party_site.com handles auth and sends a JWT back as a query string in the resulting URL. When I try to use a navigation guard (as shown below) to get the query string, Vue router only looks in the router's history.
router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '#/components/Home'
import Entry from '#/components/Entry'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/entry',
name: 'Entry',
component: Entry,
beforeEnter: (to, from, next) => {
console.log('to',to)
console.log('from',from)
console.log('next',next)
next()
},
meta: {
title: 'Entry'
}
},
]
})
The console prints the expected "to" location but the "from" location is the base path, "/", and there are no query parameters. How do I go about getting the "external" route so I can access the query parameters? The resulting console.log of "from" is below.
from
{name: null, meta: {…}, path: "/", hash: "", query: {…}, …}
fullPath: "/"
hash: ""
matched: []
meta: {}
name: null
params: {}
path: "/"
query: {}
__proto__: Object
Before asking, the app, in its current state, has to be built this way and I do not have access to the external site.

Wild shot in the dark here but the # in your nextpage query parameter is probably messing with the remote site.
What it will see is
QUERY => nextpage=http://my_site.com/
FRAGMENT => #/entry
so it will redirect back to http://my_site.com/ because it probably ignores any fragment.
You should encode the value correctly, eg
window.location = 'https://third_party_site.com/?nextpage=' +
encodeURIComponent('http://my_site.com/#/entry')
which will produce nextpage=http%3A%2F%2Fmy_site.com%2F%23%2Fentry

Related

vue router - redirect after successfull authentication

I have a vue app which uses the vue router. I implemented an authentication system for the login routes. How do I get it to redirect automatically to the route the user initially wanted to visit after login was successfull?
This is my code
import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import Home from "../views/Home.vue";
import Collections from "../views/Collections.vue";
import Login from "../views/Login.vue";
import store from "../store/index";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/collections",
name: "Collections",
component: Collections,
//meta: { requiresAuth: true }
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
router.beforeEach((to, from, next) => {
if (!store.getters.isAuthenticated && to.name !== "Login") {
next("/login");
} else {
next();
}
});
export default router;
Instead of just calling next('/login'), you can additionally pass the route that users want to visit as a query parameter, like this:
next({
path: '/login',
query: {
redirect: to.fullPath,
}
})
Then, in the login page, after the successful authentication, you can call
this.$router.replace(this.$route.query.redirect || '/');
It will redirect users to the page they initially want to visit, or to home if there is no information about redirection.
Use cookie storage
this.cookieUrl = this.$route.query.redirecturl
if (this.cookieUrl) {
cookieStorage.setItem('redirecturl', this.cookieUrl, false, 2000000)
}
and after login check cookie
if (cookieStorage.getItem('redirecturl')) {
this.$router.push({
path: cookieStorage.getItem('redirecturl')
})
cookieStorage.removeItem('redirecturl')
The plain and simple approach which could work in that case is simply redirecting back to previous route after successful login.
$router.go(-1)
Your best option is to initially redirect the person to the login page with a parameter that specifies the original page. Something like:
if(!loggedIn) {
// If not authenticated, add a path where to redirect after login.
this.$router.push({ name: 'login', query: { source_path: '/current_route' }});
}
(you can optionally use this.$route.query.page in place of an explicit /current_route string).
Then in the login page have a method that uses the parameter if set, or falls back to the homepage:
this.$router.push(this.$route.query.source_path || '/')

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>

How to open route as modal on Desktop but page on mobile in Nuxt

I am using the #nuxtjs/router module for defining custom routes. Here is my router.js file
import Vue from 'vue'
import Router from 'vue-router'
import News from '~/pages/News'
import Login from '~/pages/auth/Login'
import Signup from '~/pages/auth/Signup'
import Account from '~/pages/Account'
Vue.use(Router)
export function createRouter() {
return new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Index',
component: News,
},
{
path: '/news',
name: 'News',
component: News,
},
{
path: '/news/:tag',
name: 'TaggedNews',
component: News,
},
{
path: '/news/:id([a-f0-9]{32})/:title',
name: 'NewsItem',
component: News,
},
{
path: '/news/:tag/:id([a-f0-9]{32})/:title',
name: 'TaggedNewsItem',
component: News,
},
{
path: '/login',
component: Login,
},
{
path: '/signup',
component: Signup,
},
{
path: '/account',
component: Account,
},
],
})
}
I want to open the /login route as a modal on Desktop but page on mobile. How do I go about it?
Short answer would be you can't, as for using a modal you need to tell the app what "actual route" you are on - imagine navigating directly to /login on desktop and the issue becomes clear.
My suggestion would be to not add a route for login, but to use a query Param for whether or not the Login modal should be displayed:
query Param would be handled by a LoginModal component on the app root
closing/opening would both trigger and be managed by changes to the query parameter
On mobile, the modal can be styled as a full screen block.

Redirect to specific url in case of wrong url in vuejs

I have two separate routing files where I am importing the component and defining their routing in each of its file and using it in index.js file. Here are my files code:
//router1.js
import Layout1 from 'Layouts/Panel.vue';
const Users = () => import('Views/Users.vue');
const Reports = () => import('Views/Reports.vue');
export default {
path: '/layout1',
component: Layout1,
redirect:'/layout1/reports',
children:[
{
path: 'reports',
component: Reports,
name:'Reports'
},
{
path: 'users',
component: Users,
name:'Users'
}
]
}
//router2.js
import Layout2 from 'Layout/Panel2';
const Demo1 = () => import('Views/Demo1');
const Demo2 = () => import('Views/Demo2');
export default {
path: '/',
component: Layout2,
redirect:'/demo1',
children:[
{
path: '/demo1',
component: Demo1
},
{
path: '/demo2',
component: Demo2
}
]
}
// index.js
import Vue from 'vue'
import Router from 'vue-router'
import router1 from './router1';
import router2 from './router2';
const NotFound = () => import('Views/NotFound.vue');
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
router1,
router2,
{
path: '*',
component: NotFound,
name:'NotFound',
},
]
})
Now, I want to redirect to specific url i.e "not-found" in case of wrong URL. In "NotFound" component I am adding below line of code in mounted lifecycle hook which redirects to URL "not-found".
this.$router.replace({ path: 'not-found' });
But if URL is having parameters or query string it will append to it. For e.g- http://localhost:8080/home/not-found
What I want is that it only shows http://localhost:8080/not-found How should I achieve this. Please help. Thanks!
try this in your mounted function. worked on my side.
this.$router.push({path: '/not-found'})

Is there a way to remove a directory from a dynamic URL using Vue-Router?

I've build a vue.js web app for an insurance brokerage where every agent has their own website that is generated from their profiles.
This is what the link looks like in my vue-router index file"
{
path: '/agents/:id',
name: 'AgentSite',
component: AgentSite
},
Everything works great EXCEPT that the urls are getting too long to fit on some business cards. I would like to change the URLs to be like this:
{
path: '/:id',
name: 'AgentSite',
component: AgentSite
},
However, then every other bit of dynamic content in the app loads our agent website template (AgentSite). Quotes, Clients, Policies... they won't load properly.
Is there a way to remove the "/agents" from the URLs without messing up the rest of our application? I could shorten it to "/a/:id but that ends up being more confusing than it's worth.
Thanks!
EDIT: a couple of people have mentioned solutions that work when the agent id is a number. That's a great idea except that we have built agent "slugs" to use instead.
On the agent website layout:
created() {
console.log(this.$route.params.id);
this.$store.dispatch("getAgentFromSlug", this.$route.params.id);
}
and in the store:
getAgentFromSlug({commit}, payload){
const db = firebase.database();
db.ref("users/").orderByChild("slug").equalTo(payload).once("value",
(snap) => {
console.log(snap.val());
var info = snap.val();
commit("setAgentSiteInfo", info[Object.keys(info)[0]])
})
}
So, our route Id is really a slug.
Considering ids are numbers, you could use:
{
path: '/:id(\\d+)',
name: 'AgentSite',
component: AgentSite
},
Which only matches if id is made only of numbers.
Update: A couple of people have mentioned solutions that work when the agent id is a number. That's a great idea except that we have built agent "slugs" to use instead.
If the names can conflict with existing routes, declare the agent route last.
From the Matching Priority docs (emphasis mine):
Matching Priority
Sometimes the same URL may be matched by multiple routes. In such a
case the matching priority is determined by the order of route
definition: the earlier a route is defined, the higher priority it
gets.
In other words, declare like:
routes: [
{
path: '/',
component: HomePage
},
{
path: '/quotes',
component: Quotes
},
{
path: '/clients',
component: Clients
},
{
path: '/:id',
component: AgentSite,
props: true
}
]
See CodeSandbox demo Here.
Handling 404 pages
Would I then declare the 404 page route above or below the "AgentSite" in your example? { path: "*", component: PageNotFound }
The AgentSite route would match any URL not matched previously, so you'll have to handle the 404s inside the AgentSite component.
First, declare the 404 route after the AgentSite:
routes: [
// ... (other routes)
{
path: "/:id",
component: AgentSite,
props: true
},
{
path: ":path",
name: "404",
component: p404,
props: true
}
]
Then, inside AgentSite, get the agent :id, check if it is a known agent and, if not, redirect to the 404 route by name (otherwise it would match agent again).
export default {
props: ["id"],
data() {
return {
availableAgents: ["scully", "bond", "nikita"]
};
},
created() {
let isExistingAgent = this.availableAgents.includes(this.id);
if (!isExistingAgent) {
this.$router.push({
name: "404",
params: { path: this.$route.fullPath.substring(1) }
});
}
}
};
The CodeSandbox demo Here already contains this handling.
You can use regex matching if you :id has a specific format (example from vue-router repository).
For example, if your :id is a number:
const routes = [
{ path: '/:id(\\d+)', component: Foo },
{ path: '/bar', component: Bar }
]
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/:id(\\d+)', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
.router-link-active {
color: red;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<router-link to="/321321">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>