Dynamic Vue Router - vue.js

I am researching whether a vue router is the best approach for the following scenario:
I have a page containing 'n' number of divs. Each of the divs have different content inside them. When a user clicks on a button in the div, I would like the div to open in a separate browser window (including its contents).
Can a route name/component be created on the fly to route to? Since I have 'n' number of divs, that are created dynamically, I cannot hard-code name/components for each one
<router-link :to="{ name: 'fooRoute'}" target="_blank">
Link Text
</router-link>
I want to avoid the same component instance being used (via route with params) since I may want multiple divs to be open at the same time (each one in their own browser window)

If the link is opening in a separate window, it makes no sense to use a <router-link> component as the application will load from scratch in any case. You can use an anchor element instead and generate the href property dynamically for each div.
To answer your questions:
A route name cannot be created dynamically since all routes must be defined at the beginning, when the app (along with router) is being initialized. That said, you can have a dynamic route and then dynamically generate different paths that will be matched by that route.
There is no way for the same component instance to be reused if it's running in a separate browser window/tab.

It is possible to create dynamic router name.
profileList.vue
<template>
<main>
<b-container>
<b-card
v-for="username in ['a', 'b']"
:key="username"
>
<b-link :to="{ name: profileType + 'Profile', params: { [profileType + 'name']: username }}">Details</b-link>
</b-container>
</main>
</template>
<script>
export default {
name: 'profileList',
data () {
return {
profileType: ''
}
},
watch: {
// Call again the method if the route changes.
'$route': function () {
this.whatPageLoaded()
}
},
created () {
this.whatPageLoaded()
},
methods: {
whatPageLoaded () {
this.profileType = this.$route.path // /user or /place
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
</style>
b-container, b-card, b-link are taken from bootstrap-vue, so you can freely change it.
router.js
const router = new Router({
mode: 'hash',
base: process.env.BASE_URL,
linkExactActiveClass: 'active',
routes: [
// USERS
{
path: '/user/:username',
name: userProfile,
component: userProfile
},
{
path: '/user',
name: 'userList',
component: profileList
},
// PLACES
{
path: '/place/:placename',
name: placeProfile,
component: placeProfile
},
{
path: '/place',
name: 'placeList',
component: ProfileList
}
]
})

Related

Why is this.$route.params null?

I want to pass data to another page and I use the following code:
this.$router.push({ path: '/guard/foreigner-list', params: data});
Then I expect item is equal to data, but item is null
let item = this.$route.params;
You did not posted the entire code that is related to the process of changing route. But according to Vue Router documentation:
params are ignored if a path is provided, which is not the case for query, as shown in the examples. Instead, you need to provide the name of the route or manually specify the whole path with any parameter
So if you have defined a route called user in your router.js file like below:
import User from "../views/User"
const routes = [
{
path: '/user/:id',
name: 'User',
component: User
}
]
Then you can navigate programmatically from Home.vue to User.vue with the codes below:
Home.vue:
<template>
<div class="home">
<button #click="navigFunc">click to navigate</button>
</div>
</template>
<script>
export default {
name: 'Home',
methods: {
navigFunc: function () {
const id = '123';
// using "name" not "path"
this.$router.push({ name: 'User', params: { id } });
}
}
}
</script>
User.vue:
<template>
<section>
<h1>User page</h1>
</section>
</template>
<script>
export default {
name: "User",
mounted() {
/* access the params like this */
console.log(this.$route.params)
}
}
</script>
<style scoped>
</style>
Notice that the variable I defined (id), is the same as the params that was defined in router.js (path: '/user/:id').
Start from vue-router#4.1.4 (2022-08-22) passing object through params is no longer a viable option, since it is consider as anti-pattern.
However,
there are multiple alternatives to this anti-pattern:
Putting the data in a store like pinia: this is relevant if the data is used across multiple pages
Move the data to an actual param by defining it on the route's path or pass it as query params: this is relevant if you have small pieces of data that can fit in the URL and should be preserved when reloading the page
Pass the data as state to save it to the History API state: ...
Pass it as a new property to to.meta during navigation guards: ...

VueRouter does not receive meta property in router-link tag

I'm trying to pass meta fields via <router-link> tag. Like this:
<router-link :to="{path: '/inlineMeta', meta: {title: 'Inline meta'}}">
Inline meta
</router-link>
I want to achieve this because I can dynamically pass metas from a backend framework directly on the tag itself. Declaring routes option in a JS file simply does not have that power.
Theoretically, any object passed via to prop should be pushed to router stack right? But in this case it doesn't seem like so.
If I declare meta in routes option, it definitely works. There is no doubt about that.
I wonder if it's possible to do so, and how would I do that?
A small fiddle to illustrate the problem. Click on URLs and notice the console. I can't get StackOverflow snippet to work properly.
JSFiddle
Vue.use(VueRouter);
const cComponent = {
data() {
return {
fetchedData: null
}
},
template: `<div>The meta is: {{$route.meta.title}}</div>`,
}
const routes = [{
path: "/inlineMeta",
component: cComponent,
},
{
path: "/routeMeta",
meta: {
title: 'Meta declared in routes'
},
component: cComponent,
}
]
const router = new VueRouter({
mode: "history",
routes,
})
router.beforeEach((to, from, next) => {
console.log("The meta title is : " + to.meta.title);
})
const app = new Vue({
el: "#app",
router,
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
<script src=" https://unpkg.com/vue-router#3.5.1/dist/vue-router.js"></script>
<div id="app">
<router-link :to="{path: '/inlineMeta', meta: {title: 'Inline meta'}}">Inline meta</router-link>
<router-link :to="{path: '/routeMeta'}">Meta declared in routes</router-link>
<router-view></router-view>
</div>
If you want to pass the parameters between two components, use props.
v-bind:to in <route-link> is just used for route match, so you can't use it to set the value of meta.
If you want to set the meta without declaring it in route, i think using "this.$route.meta.title="Inline meta" " in js.
like using the click event:
<li #click="setMeta">
<router-link :to="{path: '/inlineMeta'}">
Inline meta
</router-link>
</li>
and in js:
method:{
setMeta(){
this.$route.meta.title="Inline meta";
}
}
you can also use mounted() in the new component you just routed to like :
mounted(){
this.$route.meta.title="Inline meta"
}
and meta will be modified after reloading
This may work though it may looks not that elegant.

How do you build page within a folder in Vue?

I'm trying to have my URL as /social/fb/, i've tried looking online and documentation but can't find the answer to this simple question for the life of me.
My folder structure is:
Pages
-social
--fb
I have a social.vue file in pages which works fine as www.example.com/social but can't get www.example.com/social/fb. Any direction would be much appreciated.
Create social folder inside pages, then create fb.vue inside social folder.
This should work
I normally load the layout.vue from the router and treat everything else as a child which is passed though as a router-view, this then saves having an index.vue for each parent.
But you would always want to make a directory to contain the social pages. Then would be a case of simply adding to the router.js file.
layouts/template.vue
<template>
<router-view></router-view>
</template>
<script>
export default {
name: 'layout-template'
}
</script>
router.js
...
/*
* Social
*/
{
path: '/social',
component: () => import('./layout/template.vue'),
props: true,
// rendered inside <router-view>
children: [{
path: '/',
component: () => import('./pages/social/index.vue')
}, {
path: 'fb',
component: () => import('./pages/social/fb.vue')
}, {
path: 'twitter',
component: () => import('./pages/social/twitter.vue')
},
// or do something more dynamic
{
path: ':network', // accessible in component though `$route.params.network`
props: true, // makes accessible though `props: ['network']`
component: () => import('./pages/social/network.vue')
}]
},
...
./pages/social/index.vue - could show something /social homepage or change route to import('./pages/not-found.vue') instead.
./pages/social/network.vue
<template>
...
</template>
<script>
export default {
name: "page-social-network",
props: {
network: {
type: String,
default: ''
}
},
created() {
// or through
this.$route.params.network
}
};
</script>
<style lang="scss" scoped></style>
See: https://router.vuejs.org/guide/essentials/passing-props.html#boolean-mode
Otherwise is just standard vue pages

Sending variables from one view to another view and url in vue

What I want is when I click an image, it redirects to a URL (another view) which is made by the something/image_name. I am making the project in vue.js. I am thinking of doing this by using props (passing all the variables needed from the first view to the next view). But the data is not displayed in the 2nd view. Also, I want to know how can I make the URL like something/image_name. I am using it by hardcoding the URL.
routes.js
const router = new Router({
mode: "foo",
base: "foobar",
routes: [
{
path: "/event/:title",
name: "event",
component: event,
props: true,
}
where title is the variable (event.title to be more precise) I want to pass from other view. I also want to get title in URL also.
view 1
<template>
<div src="image_location" :to="/event/{{event.title}}"></div>
</template>
<script>
export default {
name: "event",
components: {
Event
},
data: function initData() {
return {
event: {},
};
},
};
</script>
view 2 (Event.vue) (its URL should be foobar/event/{{ title }})
props: {
event: Object,
}
I tried router-link also but lack of its documentation restricts me from using it efficiently.
You can add an #click event to the image and programmatically move the user to the new URL when they click on the image. Something like:
<imge src="..." #click="$router.push(`/event/${event.title}`)" />

Navigating to Child Components with Vue.js

I have an app built with Vue.js. In this app, I'm using the Vue Router. I successfully have top-level routes working. However, I'm unable to get child routes to work. My code seems to ignore any routes defined within the children property of a route. I've setup the problem in this fiddle. The relevant code looks like this:
const CustomerMenu = { template: '<div><br />please choose: <router-link to="/customers/list">list</router-link> <router-link to="/customers/stats">stats</router-link></div>' }
const CustomerList = { template: '<div>A list of customers here</div>' };
const CustomerStats = { template: '<div>Customer statistics</div>' };
const Suppliers = { template: '<div>Suppliers</div>' }
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/customers', component: CustomerMenu, children: [
{ path: 'list', component: CustomerList },
{ path: 'stats', component: CustomerStats }
]},
{ path: '/suppliers', component: Suppliers }
]
})
new Vue({
router,
el: '#app',
data: {}
});
How do I show the content associated with a route defined in the children property of a route. In other words, how do I get the "list" or "stats" components to appear when a user selects there respective links?
Thank you!
You were missing <router-view/> inside your CustomerMenu component.
http://jsfiddle.net/sn5vu6ek/
Remember - every component which has children routes has to have to be able to display children components.
If you just want to have some part of path common between components, you can either create parent with just <router-view/> inside, or specify full path for multiple components.