Svelte Dynamic Route Param - Passing Multiple Params? - dynamic

Is there a way to pass multiple params into a dynamic route?
For example I have a search result page that is looping through results
{#each results as result}
<a href="../book/{result.key}">
{result.title}
</a>
{/each}
I am passing the key to a [slug] route and I am using the key to call an API.
My end goal is to have the result.title be the dynamic route param, but I also want to pass the result.key so I am able to call the API.
This post: Passing mulitple parameters to dynamic route in Svelte is over a year old and I was wondering if there is now a way to do this and I would like to keep the route as /title instead of /title/key or /key/title as suggested in that post.

You will have to embed both of them somehow in the URL. Perhaps a construction like /book/<title>?key=<key> would do for you ? I notice that would be the 'other option' from the post you linked, but the answer there is outdated.
Nowadays you would either in +page.sveltejs or +page.server.js do
export const load = (async ({ params, url }) => {
const title = params.title;
const key = url.searchParams.get('key');
}
But here you would have to be aware that key could be empty.
One last option (that I personally don't like) is to have the urls in the form of /book/<title>-<key>, this is something you sometimes see with product sites.
In this case your file will be called /book/[title]-[key]/+page.svelte
And you just extract the params as normal:
export const load = (async ({ params }) => {
const { key, title } = params;
}

Related

How to catch all routes starting with admin

In my vuejs 3 application, I'm trying to add a navigation drawer to all admin routes. So, I need to catch all routes starting with admin (this includes "/admin", "/admin/users", "/admin/users/10" etc). I tried "/admin*", not working. I've tried googling around, no solution too.
One possible approach:
In the component where you render the navigation drawer, you could extract the route path portion that you need and then check if the current route is allowed to render the navigation drawer:
So you could create a function like this:
import { useRoute } from 'vue-router';
const route = useRoute()
//We will pass a parameter to reuse the function in case you need it.
const routeIsAllowed = (requiredRoute) => {
const routePath = route.path.substring(0, requiredRoute.lenght);
return routePath === requiredRoute
}
Then in your template you just render conditionally based on the route path name matching your criteria:
<NavigationDrawer v-if="routeIsAllowed('/admin')" />
There might be a different approach but I think is a valid solution with reutilization in mind.
Also if you want to get an array of routes in a nested route configuration in Vue Router, you just need to get it like this:
full route path: /admin/users
route.matched[0].path // '/admin'
route.matched[1].path // '/users'
Basically matched will give you an array of each route path, which can be accessed with the index value and .path at the end as a string.

How do I pass parameters with router.push in vue.js?

I'm working on a vue.js application. I'm trying to pass parameters in a redirect from one component to another like this:
this.$router.push({
path: '/my-path',
query: {
anArray: [...]
}
});
Once the component at my-path loads, I'm able to retrieve the parameters like this:
const theArray = this.$route.query.anArray;
The problem is that as soon as I refresh the page, the parameters are gone. When I open Chrome DevTools and put a break point where I retrieve the array from $route.query, I see this:
0: "[object Object]"
1: "[object Object]"
2: "[object Object]"
It seems obvious that it's getting this from the url which is:
http://localhost:8080/my-path?anArray=%5Bobject%20Object%5D&anArray=%5Bobject%20Object%5D&anArray=%5Bobject%20Object%5D
It doesn't seem to realize the 'object' terms in the url are just encodings of actual objects, which are available from $route.query the first time it loads.
Is there another way to pass parameters to a component using $router.push() such that the parameters persist on the page even after refreshing?
I could just try:
this.$router.push('/my-path?anArray=[...]');
...but my goal is to hide the parameters from the user (so don't show them in the url). This is another reason I'm looking for an alternate way of passing parameters (it doesn't even hide them).
I also tried:
this.$router.push({
path: '/my-path',
params: {
anArray: [...]
}
});
...but this made the parameters unavailable in the component (I wonder if this has anything to do with our global router which routes '/my-path' to the MyPath component without specifying parameters; is it wiping out the parameters?).
Thanks.
If you want to hide the parameters from the user, you must not use query. Instead, you should use parameters. Here I let you an example:
//routes.js
path: '/:data',
name: 'Home',
component: () => import('pages/YourPage.vue')
//Passing parameters
this.$router.push({
name: 'Home',
params: { data: yourData}
});
//Receiving parameters in Home component
created() {
console.log('Params: ', this.$route.params);
}
I hope this could be usefull
While params suggested #elC0mpa is a correct answer here are some alternatives depending on the use case:
localStorage/SessionStorage
Save the paramters into localStorage/SessionStorage and retrieve them in the loading sequence of your destination-page as #nachodd pointed out.
⚠ It should be noted that only key value pairs in form of strings are being saved into these storages.
You will need something along the line of
localStorage.setItem(itemKey,JSON.stringify(itemValue)
to set the values and
const itemValue = JSON.parse(localStorage.getItem('itemKey')
to recive it. Also localStorage.removeItem('itemKey') for cleanup.
vuex store
You may want to consider saving your data in the store all together and access it from any vue-component.
The commands needed are this.$store.commit('pathToStore/setterForKey',value) to save something into the store and this.$store.getters[pathToStore/getterForKey'] to receive the items value.
⚠ It should be noted that you need to set up the store accordingly with every state setter/mutation, getter and action. See this documentation.

Watch for URL query parameter in Vuex store

I am using Nuxt.js with Vuex and I would like to trigger a mutation when somebody enters in my web with a certain parameter (ex: https://example.com/?param=abc), and pass the parameter to a state.
I tried to check the documentation of the watchQuery property https://nuxtjs.org/api/pages-watchquery, but there’s no examples about how to do this, I just found this How to watch on Route changes with Nuxt and asyncData but I can’t see any way of how to write an action in Vuex store with watchQuery.
I tried writing:
actions: {
watchQuery: true,
asyncData ({ query, app }) {
const { start } = query
const queryString = start ? `?start=${start}` : ''
return app.$axios.$get(`apps/${queryString}`)
.then(res => {
commit('setParam',res.data);
})
},
}
But that syntax is not allowed.
Any help would be welcome, thanks in advance!
From my understanding watchQuery sets a watcher for query string, meaning it's waiting for the query to change while the page is already rendered making it possible to call methods like asyncData() again.
Since you only want to save a certain parameter when the user enters the page and then pass the paramater to a state you just need to move your asyncData method to a page from which you want to get the parameter, you will also need to extract store and query from the context automatically passed into asyncData and then using the store and query save the query parameter into your state.
Here is a simple demonstrantion
// Your page from which you want to save the param
export default {
asyncData({store, query}) { // here we extract the store and query
store.state.somethingForSavingTheParam = query.nameOfTheParamYouWantToSave
// instead of using store.state you could use store.commit(...) if that's what you want
}
}

How to deal with fragment GET parameters in Vue Router?

After trying a backend implementation of Gitlab's OAuth using Web Application Flow, I'm trying out Implicit Grant in my Vue frontend.
The access code that the API sends me is in fragment url parameter format. Which means it uses the hash symbol #foo=bar&moo=var instead of standard ?foo=bar&moo=var which conflicts with the default Vue Router configuration.
After trying history mode in the Vue Router, which dosent use the # in his urls, I can access the Vue Router parameter with $route.hash. My current problem is that I have to split and trim the parameters myself with something like the following:
const params = this.$route.hash.split('&').map(part => part.replace(/#/, ''));
const parsedParams = {};
params.forEach(param => {
const parts = param.split('=');
parsedParams[parts[0]] = parts[1];
});
which outputs { foo: "bar", moo: "var" }
Is there a better or more standart way to deal with fragment parameters in VueRouter? If not, is there a standart solution to this outside of Vue?
It seems to me that my code snippet is a good enough solution, although I was expecting some support for OAuth standarts in vue. Maybe my expectations are too high?
This is what I ended up using
const parsedParams = {};
this.$route.hash.split('&')
.map(part => part.replace(/^#/, ''))
.forEach(param => {
const parts = param.split('=');
parsedParams[parts[0]] = parts[1];
});
you can also uri decode your parameters directly from within this loop using
parsedParams[parts[0]] = decodeURIComponent(parts[1]);

Express: pass variable to main layout

I'm starting to learn Express and I'm stuck trying to pass a variable in my default layout, that gets loaded every time I render a view, so I don't have to pass it every time I res.render something, like below:
router.get('/', function(req, res, next) {
res.render('home', { title: 'title1', 'isuser':req.user});
});
router.get('/loggedin',function(req,res){
res.render('loggedin', {title: 'title2', 'isuser':req.user});
});
router.get('/register', isLoggedIn, function(req,res)
{
res.render('register', {title: 'title3', 'isuser':req.user});
});
I don't want to pass isuser every time I render, that's why I want to just pass it to my main layout so the information is used automatically.
The default layout isn't explicitly rendered in code (with res.render) but acts like a frame for all rendered views and gets loaded automatically. In my case I use handlebars and I load the default layout like so, in my main app.js:
app.engine('handlebars', expbhs({defaultLayout:'defaultLayout'}));
In Node, you can simply create endpoints server-side, like this:
app.get('login/', function(request, response){
response.render('loginpage', { any data you need });
});
app.get('logout/', function(request, response){
response.render('logoutpage', { any data you need });
});
loginpage and logoutpage are in your views folder and can be jade, handlebars, or any other template. The data to be filled in can be passed as shown above, possibly extracted from the request.
In your HTML you can simply link to your 'login' page (not '/login'), and this should then return your login page.
I realize this question is a couple years old, but I had the exact same question. I think the OP is using express-handlebars (given the "expbhs" abbreviation), so this documentation should be relevant: https://www.npmjs.com/package/express-handlebars#helpers
When you create your instance of express-handlebars, you can pass it some parameters such as defaultLayout and a list of helper functions to generate the data you want to insert into your template.
In my case, I was trying to display the current year in the main template's footer (which appears on every page). Like OP, I did not want to pass this data into every single route.
var handlebars = require('express-handlebars').create({
defaultLayout:'main',
helpers: {
year: function() {
return new Date().getFullYear();
}
}
});
This results in displaying the year where I've written {{year}} in main.handlebars. Hope this helps someone else.