Customize the route prefix pattern using Vue-Router - vue.js

I am trying to use Vue-Router to pattern URLs in my new project so they look just like URLs in an existing (non-Vue) application. Existing application URLs look like this:
/#!page1
/#!page2
My Vue Router currently looks like this:
const router = new VueRouter({
routes: [
{
path: '/',
redirect: '/page1'
},
{
path: '/page1',
component: Assessments
},
{
path: '/page2',
component: Assessments
}
]
})
But, of course, this generates URLs that look like this:
/#/page1
/#/page2
How can I configure my router to mimic the /#!route pattern?

You can use the base option:
base
type: string
default: "/"
The base URL of the app. For example, if the entire single page
application is served under /app/, then base should use the value
"/app/".
https://router.vuejs.org/en/api/options.html#base
I have tried it with your case, it also works with /#!.

Related

How to use a single route in different contexts?

In Nuxt, I have an admin dashboard with a special layout (sidebar), where I use <NuxtChild> to render child routes:
Admin.vue
<NuxtChild :key="$route.path" />
Routes (simplified):
{
path: "/admin",
name: "admin",
component: Admin,
children: [
{
path: '/admin/event/create',
name: 'EventCreate',
component: EventCreate,
props: true
}
// many more routes...
]
}
Now, I want the EventCreate route to also be available alone, in a regular isolated context (NOT in the admin dashboard). This is simple enough with another route. This works fine:
{
path: '/event/create',
name: 'EventCreate',
component: EventCreate,
props: true
}
PROBLEM:
My routes config file will be too messy, with duplicated routes that essentially only differ by path.
Note: I do not use Nuxt's standard file-based routing. Instead all of my route's are defined in one central config file (for many reasons, and my preference). This is done using the Nuxt-Community router library: https://github.com/nuxt-community/router-module. The end result is essentially how Vue-Router works (ie routes defined in a config file).
QUESTION:
Is there a way to define a route once, and have it apply to different contexts (alone or as a child inside another route)?
On a higher level perhaps there's a better way to handle this context switching (plain page vs child-inside-dashboard). In any case, <NuxtChild> works well aside from this, and I wanted to keep using it.
Any suggestions; different idea?
it's easy you can use alias option of vue-router:
{
path: "/admin",
name: "admin",
component: Admin,
children: [
{
path: 'event/create', // not repeat path from root
alias: ['/event/create'],
name: 'EventCreate',
component: EventCreate,
props: true
}
// many more routes...
]
}
and one more note never use a path that starts with / and repeat the parent path in children it doesn't work at all it's verbose, confusing and potentially error-prone
Share route properties with an object
Based on comment by #Estus Flask, here's one way to have a cleaner routes definition:
Use a object to store shared properites, and use it in relevant routes, reducing duplication. Still, you must still have separate routes for contexts, which makes sense.
Object to hold shared properties:
const MyEventCreateRoute = {
component: EventCreate,
props: true,
// other stuff
}
Routes use the shared properties with spread operator:
// Basic route; not in dashboard:
{
path: '/event/create',
name: 'EventCreate',
...MyEventCreateRoute
}
// Route in admin dashboard; renders inside <NuxtChild>:
{
path: "/admin",
name: "admin",
component: Admin,
children: [
{
path: '/admin/event/create',
name: 'AdminEventCreate',
...MyEventCreateRoute
}
]
}

vue application in php page with router hotlinking

I am currently building a module for a CMS (Joomla) - the frontend of this module is created with VUE 3 incl. Router. The prototype is already working and can be integrated into the CMS Module. Also the router works. If a link is clicked within the VUE app, the corresponding view is displayed. But if the user is now on a subpage of the VUE App and refreshes it (F5 / Ctrl + F5), the page is not found - i think because it exists only in the Vue Router.
To the URL structure:
domain.tld <-- This is where the Vue application is located.
domain.tld/list-:id/item-:id <-- This is the URL for an ITEM
Now I know that it doesn't work like that because the webserver tries to interpret the URL which it can't because parts of it are from VUE.
Is it possible to reconfigure the vue router to work with parameters instead of a "physical" structure?
from: "domain.tld/liste-:id/item-:id"
to: "domain.tld?liste=:id&item=:id"
i think this could solve the issue but i dont know...
Edit:
When i try to use this in the router it still works but has the same effect because yeah "appname" cannot be found by the server..
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/appname?playlist=:id',
name: 'PlaylistDetails',
component: PlaylistDetails,
props: true
},
{
path: '/appname?playlist=:id&video=:vid',
name: 'Player',
component: Player,
props:true
},
]
You can assign a controller to a wild-card, which always return you Vue app:
$router->addMap('/domain.tld/*', 'VueController');
Another approach would be using a # in your URL. Everything after your # will be ignored by the server.
Based on the information i've got from Roman i have changed the routes and added a 404 to the router which refers to home. The views are now been loaded as "url params".
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/#appname?playlist-:id',
name: 'PlaylistDetails',
component: PlaylistDetails,
props: true
},
{
path: '/#appname?playlist-:id&video=:vid',
name: 'Player',
component: Player,
props:true
},
{
// Match all paths vue2 Use * vue3 Use /:pathMatch(.*)* or /:pathMatch(.*) or /:catchAll(.*)
path: "/:pathMatch(.*)*",
name: "404",
component: Home
}
]
If now someone tries to open a site via directlink he got redirected to home.
There might be a better solution but this works when you are using vue inside another PHP app where you are not able to configure the server.
additional info for 404:
https://qdmana.com/2020/12/20201223195804176T.html
It looks that Hotlinks (directly into a view) are not possible in my scenario.

Custom handling forward slashes in vue router ids

I have a use case for needing the id part of a vue route to contain unescaped forward slashes.
My current route looks like this:
{
path: '/browse/:path*',
component: browse,
name: 'browse',
displayName: 'Browse',
meta: { title: 'Browse' },
},
So when a user browses to the above url, the browse component is shown.
However, i want to use the id part of the path (:path*) to contain a nestable fielsystem like path to be consumed by my browse page.
For example the url /browse/project/project1 would take me two levels down in my tree to the project1 item.
Now, the problem i'm running into is that vue router is escaping my ids (path) when navigating programatically, and my url ends up like this: /browse/project%2Fproject1. This is non-ideal and does not look nice to the end user. Also, if the user does browse to /browse/project/project1 manually the app will work correctly and even keep the original encoding in the url bar.
So i could resolve this my making an arbitrary number of child paths and hope that the system never goes over these, but thats not a good way to solve my problem.
I should also clarify that the application will not know anything about the path after /browse as this is generated dynamically by the api that powers the app.
Is there a native way in vue-router to handale this? or should i change up how im doing things.
There is a more elegant solution without workarounds.
Vue router uses path-to-regexp module under the hood and constructions like
const regexp = pathToRegexp('/browse/:path*')
// keys = [{ name: 'browse', delimiter: '/', optional: true, repeat: true }]
https://github.com/pillarjs/path-to-regexp#zero-or-more
const regexp = pathToRegexp('/browse/:path+')
// keys = [{ name: 'browse', delimiter: '/', optional: false, repeat: true }]
https://github.com/pillarjs/path-to-regexp#one-or-more
set repeat flag to true. Any array parameter with repeat flag will be joined with the delimiter (default '/').
So you can pass a splitted array ['project','project1'] instead of 'project/project1' into router.push():
router.push( {name: 'browse', params: {path: ['project','project1']}} );
or
router.push( {name: 'browse', params: {path: 'project/project1'.split('/')}} );
So I managed to 'fix' this with a bit of a hack.
When creating my Vue router instance I am attaching a beforeEach function to replace any outgoing encodings of '/'. This will send the 'correct' URL I am looking for to the client.
const router = new Router({
mode: 'history',
routes,
});
router.beforeEach((to, from, next) => {
// hack to allow for forward slashes in path ids
if (to.fullPath.includes('%2F')) {
next(to.fullPath.replace('%2F', '/'));
}
next();
});
I just stumbled over your question while facing a similiar problem.
Think this is because an id shall identify one single resource and not a nested structure/path to a resource.
Though I haven't solve my problem yet, what you probably want to use is a customQueryString:
https://router.vuejs.org/api/#parsequery-stringifyquery
https://discourse.algolia.com/t/active-url-with-vue-router-for-facet-and-queries/3399
I fixed it by creating helpers for generating hrefs for :to attributes of vue router link.
First i made router accessible for my new helper service like here Access router instance from my service
Then i created router-helpers.js and here i made my helpers, here is an example
import Vue from 'vue'
import router from '../router.js'
// replace %2F in link by /
const hrefFixes = function(to) {
return to.replace(/%2F/g, '/')
}
// my link helper
Vue.prototype.$linkExample = attr => {
// create "to" object for router resolve
const to = { name: `route-name`, params: { param1: attr } }
// this will resolve "to" object, return href param as string
// and then i can replace %2F in that string
return hrefFixes(router.resolve(to).href)
}
Just include this service once in your Vue application an then just use this helper like this
<router-link :to="$linkExample(attr)">text</router-link>

How base option works in vue-router

As par documentation of base option:
The base URL of the app. For example, if the entire single page application is served under /app/, then base should use the value "/app/".
But I have tried it like following, It does not seems to work:
const router = new VueRouter({
base: "/app/",
routes
})
Demo fiddle.
The base has a default value of '/'. Drawing analogy from how it is used to route:
<router-link to="home">Home</router-link>
or
<router-link :to="{ path: '/abc'}" replace></router-link>
I just omitted the /app and it works. The base doesn't need to be part of the router-link
EDIT
Use of base in vue-router
(For this test I had used vue-cli with the webpack template.)
I had my router configurations like so:
export default new Router({
base: '/app',
mode: 'history',
routes: [
{
path: '/',
name: 'RangeInputDemo',
component: ComponentDemo
}
]
})
Adding base as '/app' made no difference to the routing that happened throughout the project, as if the base was still set to '/'.
I tried to change the url from the server side (the url at which the project is being served).
So in dev-server.js where :
var uri = 'http://localhost:' + port
controls the url of the app, I made a slight modification to:
var uri = 'http://localhost:' + port + '/app'
This caused the application to show:
Notice the fullPath being '/' in the vue console (second image).
Just for double checking, I changed the base to '/' again.
So, the base property of the router configuration is to set the base url as set by the server, if the server serves the application at a route other than '/' then the base can be used for having the application be run from the set url.
Since the question requires the routes being moved under /app, I think having /app as the parent route would be the solution in that case, if the server isn't supposed to change the route on which it serves.
In the Vue Router 4, you set base path by history api:
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
Then each path would be prefixed with process.env.BASE_URL
Had the same problem, setting "base" didn't work - workaround is to update base url in router:
{ name: 'listings', path: baseUrl + '/listings', component: PageListings}
and refer to routes by name:
<router-link :to="{ name: 'listings' }" exact>Listings</router-link>
I created a redirect on the base path to another route.
{
name: "Default",
path: '/',
redirect: { name: 'OtherRouteName' }
}
Reference:
https://next.router.vuejs.org/guide/essentials/redirect-and-alias.html

How to Set Root/Base URL for a VueJS Project

I've deployed a VueJS project to a domain like www.example.com, however, I want to move it to a subfolder so that I can access it like www.example.com/v1
How can I set the base URL or root URL of a VueJS project?
Note: It's not about the base URL for Vue-resource
You can use option base as /v1/ to move all routes to have base as /v1/.
The base URL of the app. For example, if the entire single page application is served under /app/, then base should use the value "/app/".
Old
How about moving all your routes to nested route, with parent route being /v1:
const router = new VueRouter({
routes: [
{ path: 'v1', component: BaseView,
children: [
{
// UserProfile will be rendered inside BaseView's <router-view>
// when /v1/profile is matched
path: 'profile',
component: UserProfile
},
{
// UserPosts will be rendered inside BaseView's <router-view>
// when /v1/posts is matched
path: 'posts',
component: UserPosts
}
]
}
]
})