I made this little app which is a simple Vue serverless SPA. I wish to pass an array of strings and a an array of numbers through the URL so that I can share "states" of the websites with colleagues. I understand vue-routercan update the route's parameters as per their documentation, but I do not have enough perspective to see how to implement this to solve my problem. I would love some help or guidance so I actually learn from this. Thank you all.
EDIT: after Mr. Luis Brito's hint.
I added the following to my code (props)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: require('#/views/Home').default,
props: { myNumbers: [1,2,3]}
},
]
})
in a component I did
mounted () {
this.$router.push({ name: 'myNumbers', params: {myNumbers: [1,2,3,4] }})
const myNumbers = this.$route.params.myNumbers
console.log(myNumbers);
}
But now my App throws a Vue-Router error [vue-router] Route with name 'myNumbers' does not exist but it does console log the numbers I pushed. Is it possible to make my app look for props and only if they are there to do something with them? Otherwise I get a white screen.
The way I see to solve this problem is use vue-router with a route that passes props to the component, that prop can be an object containing the two arrays that you mentioned.
Referer to Vue Router - Obect Mode
Here is an example for the router:
const routes = {
path: '/promotion/from-newsletter',
component: Promotion,
props: { myNumbers: [1,2,3,5,8], myStrings: ['first', 'second', 'third']
}
}
On the component side, you can access those props on created() lifecycle hook, or mounted()
props: ['myNumbers','myStrings '],
mounted() {
if ( this.myNumbers !== undefined && this.myStrings !== undefined ) {
console.log(thys.myNumbers, this.myStrings);
}
}
So that http://localhost:8080/?myStrings=layer1,layer2,layer3&myNumbers=-1,2,3-4 would console log layer 1-3 and the numbers.
EDITED
TO pass the values programatically, would be better to use Function Mode to capture the URL params and pass it to component. Another way would be to create a component where you can input the numbers and strings that you want, and then call the router and pass those values to the route for the final destination component.
Related
I'm implementing an paginated list. Therefore I'm using query parameters like ?size=10. This query paramter needs to be always inside my URL (like /home?size=2).
This apporach is not working:
const routes = [{ path: "/home", query: { size: "10" }, components: MyPage }];
const router = createRouter({
history: createWebHistory(),
routes,
});
I thought it is instantiating the route with some parameters. Looking into the Routing section of vue devtools shows me an empty query object:
$route:/home
fullPath:"/home"
path:"/home
query:Object (empty)
hash:""
name:undefined
params:Object (empty)
matched:Array[0]
meta:Object (empty)
redirectedFrom:undefined
href:"/home
How can I set a default query param to my route?
Here is a proposal using the beforeEach NavigationGuard:
const routes = [{ path: "/home", name: "Home", components: MyPage }];
const router = createRouter({
history: createWebHistory(),
routes,
});
router.beforeEach((to, from) => {
if(to.name === "Home" && !to.query.hasOwnProperty("size")){
to.query.size = "10"
}
})
The idea is to add to the route a default query parameter for size when it is not there.
The only way to achieve this properly without defining the default value in every router.push() is probably with Navigation Gurads. You can use them to get the query-parameters that are currently in the url, add the default query-params you need and then return the new route.
Though I would not do it, this is probably the easiest way to achieve this.
And if you want to make them customizable via pinia/vuex you would need to set up a store and make a user input to change the setting.
Note: Pinia is most likely the better option, because it is newer and will still be supported far in the future
Hi beautiful Vuejs developers out there!
I have a little problem with routing many Vue components/pages dynamically. In this scenario I am using nested routes to have a couple of routes for my layout components and hundreds of child routes for my pages and as you can imagine I'll have to type many child routes statically or manually, and then add more when I need more child routes in the future code changes but I need a solution to simplify/solve this problem with more efficient/better way like adding those routes from what user types after the layout in the url... here is my example code code:
const routes: RouteRecordRaw[] = [
{
{
path: '/student',
component: () => import('layouts/StudentLayout.vue'),
children: [
{
path: 'dashboard',
component: () => import('pages/student/Dashboard.vue'),
},
{
path: 'profile',
component: () => import('pages/student/Profile.vue'),
},
],
},
}
As you see in this code I have a layout named Student and it has two children but I'll have to type manually hundreds of child routes for this layout and other layouts is there any way to dynamically set up those routes with what users enter after the layout name like /student/dashboard or /layout/page and match it with a component name? I mean like params in Angular, can I use the param value itself inside the router to say?
{
path: ':pagename',
component: (pagename) => import('pages/student/' + pagename + '.vue'),
},
let me know if there is an efficient way to solve this problem.
Thanks in advance!
I would, personally, not use this, or advise such an approach, nor have I done it, but this idea came to me when I read your question:
My best guess would be to have a handler component which renders a component dynamically based on a route parameter (say you have /:subpage as a child to /student and the handler component is set to that route), and an exception handler around that to show a 404 page when the user types in an inexistent/unsupported route.
For example, I would dynamically import the component by the route parameter into a predefined let (e.g. let SubpageComponent; outside the try catch block), have a try catch block around the dynamic import assignment for the respective error where catch would set the variable to a 404 page. Then I would add the SubpageComponent into the data() of the component doing the rendering of the route.
Edit
I've written out come code that, maybe, makes sense.
It's based on https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
your routes definition, changed
const routes: RouteRecordRaw[] = [
{
path: '/student',
component: () => import('layouts/StudentLayout.vue'),
children: [
{
path: '/:subpage',
component: () => import('pages/student/SubpageRenderer.vue'),
props: true,
},
],
},
]
SubpageRenderer.vue
<script>
export default {
props: ['subpage'],
data() {
return {
currentSubpage: () => import(`./${subpage}.vue`)
}
}
}
</script>
<template>
<component :is="currentSubpage"></component>
</template>
Instead of using the currentSubpage import, you can also use the subpage route prop to bind :is if subpage is the name of a registered component.
Since this would get only "dashboard" from the route, you'd need some namespacing, like "student-dashboard" with the help of template literals. You could make currentSubpage into a template literal that creates the student-${subpage} name.
I'd probably recommend importing the options object of the component designated by the subpage route parameter instead of registering all the components - if you're registering them, you might as well use vue-router the usual way :)
Also, I only think this could work! It should be tested out, and perhaps casing should be kept in mind, and maybe the Layout suffix as well (subpage will probably be all lowercase, and you'll probably have the components named in PascalCase). After uppercasing the first letter, this could also obviously lead to both /student/Dashboard and /student/dashboard being valid routes
I'm creating a SPA where after each HTTP response, the app goes to the same component with the same route, but the props passed to the component will be different based on the response. I understand the reuse feature in Vue and it won't reload. But I need the newly returned data every time I trigger the router to go to the component. My app.vue:
upload(formData).then(x => {
router.push({
name: 'Chart',
params: {
chartData: x
}
});
});
Then the target Chart.vue:
props: ['chartData'],
data: function() {
// do some processing using props
return {...}
}
My problem is that since for my router, the route doesn't change, hence other approaches like using :key=$route.fullPath or the beforeRouteUpdate don't work. Then how can I make the Chart component recompute the data? I've checked thru computed() and watch as well but it's not working.
I have a bit of a specific usecase here. I have a large Vue app with lots of pages. Each page has a page-id to identify it on the server and load it's content. These ids are in the router path (parent-id/child-id/another-child-page-id). On the page components themselves I display a page title. Unfortunately, there is no correlation between the translation key of the page title to the page-id.
Now, instead of creating a mapping between page-ids and page title translations that I will have to maintain by hand, I figured I could lookup the route in the router config and require the component:
{
path: 'the-page',
name: 'ThePage',
component: resolve => require.ensure([], () => resolve(require('#/pages/ParentPage/ChildPage/ThePage')), 'parent-page')
}
This works by recursively walking through the routes to find the page-id, then loading the component in a promise:
// find the route definition
const route = findRouteByPath(file.areaName)
// load the route component
new Promise(resolve => { route.component(resolve) })
.then(component => {
console.log(component)
})
This gives me the component like so:
Object {
mixins: Array(1),
staticRenderFns: Array(0), __file: "/the/file/path.vue",
beforeCreate: Array(1),
beforeDestroy: Array(1),
components: Object,
mixins: Array(1),
render: function (),
// etc.
}
If I call the render function, I get an error:
Uncaught (in promise) TypeError: Cannot read property '_c' of undefined
What I want to do is programmatically render the component and grab the content of the slot page-title from the component.
Can anyone help with this?
Update 1
Vue.extend() then mounting seems to do what I want. The problem is, I get all kinds of errors because the pages depend on certain things being around, like the current route. I'm also a bit worried to trigger certain created() hooks that would trigger API calls and such. My current state is trying to get rid of mixins and hooks like so:
const strippedComponent = Object.assign(component, {
mixins: [],
components: {},
beforeCreate: []
})
const Constructor = Vue.extend(strippedComponent)
const vm = new Constructor({})
But I'm not quite there yet.
Update 2
I ended up abandoning this and bit the bullet by adding a meta: {} object to each route where I store things like the page title translation key and other good stuff.
I'm working on an app in Vue.js using Single File Components and Vue Router. I have a Search component where I need to execute a method to re-populate search results each time a user visits the route. The method executes correctly the first time the route is visited because of the "create" hook:
created: function() {
this.initializeSearch();
},
However, when the user leaves the route (to register or log into the app for instance), and returns to the Search page, I can't seem to find a way to automatically trigger this.initializeSearch() on subsequent visits.
Routes are set up in index.js like so:
import Search from './components/Search.vue';
import Login from './components/Login.vue';
import Register from './components/Register.vue';
// Vue Router Setup
Vue.use(VueRouter)
const routes = [
{ path: '/', component: Search },
{ path: '/register', component: Register },
{ path: '/login', component: Login },
{ path: '*', redirect: '/' }
]
export const router = new VueRouter({
routes
})
I gather that I should be using "watch" or "beforeRouteEnter" but I can't seem to get either to work.
I tried using "watch" like so within my Search component:
watch: {
// Call the method again if the route changes
'$route': 'initializeSearch'
}
And I can't seem to find any documentation explaining how to properly use the beforeRouteEnter callback with a single file component (the vue-router documentation isn't very clear).
Any help on this would be much appreciated.
Since you want to re-populate search results each time a user visits the route.
You can use beforeRouteEnter() in your component as below:
beforeRouteEnter (to, from, next) {
next(vm => {
// access to component's instance using `vm` .
// this is done because this navigation guard is called before the component is created.
// clear your previously populated search results.
// re-populate search results
vm.initializeSearch();
})
}
You can read more about navigation guards here
Here is the working fiddle