N
Nuxt8mo ago
Gobbo

most reusable and least messy way to link query parameters to refs

So typical search design - you change a certain search term, whether that be a boolean, dropdown, search query etc - I'd ideally like that to be binded with the query parameters in your URL so that you can copy and paste it elsewhere for someone else I understand the basic way to do this Have the ref default to the route.query.my_key - then use a watch on the reference and when it updates push to the router. This is good sure - however I have several inputs and having a watch for every single variable will be messy and ugly - especially since they're doing the same thing. I'm asking on whats the best way to make this a lot of cleaner and more reusable for other pages with search filters. I was hoping to create an easy to use composable but wouldnt know where to start with watches and such as its not delved in territory for me so Im asking for pointers and or ideas :)
46 Replies
pyplacca
pyplacca8mo ago
You could merge all the input refs into a single set/get computed state. The get reads and returns the url’s query params, and the set updates the url
Gobbo
GobboOP8mo ago
so keep the input refs, have a computed state in which the getter returns a string of the query params and the setter sets it? how would i go about doing the watch for the input still - as id need to check for updates might b e reading this wrong
pyplacca
pyplacca8mo ago
You wouldn’t need a watcher in this case. The get watches it’s dependencies ie; the route No, the getter would return an object where each field maps to each input
Gobbo
GobboOP8mo ago
okay yeah thought so, ill give it a shot and come back if i get lost i assume you mean <input v-model="computed.my_key" /> then the setter essentially updates the url with the updated values ( goes through the object )
pyplacca
pyplacca8mo ago
Exactly
Gobbo
GobboOP8mo ago
cool got it, will experiment and give it a shot :) appreciate it boss
pyplacca
pyplacca8mo ago
Awesome. You’re welcome
Gobbo
GobboOP8mo ago
with the setter, would I have to update the values or can I have it just push the router? In my getter im returning an object of refs ( i assume thats the right course ) and then it should just handle it like that, and the setter is only used for updating the route, it doesnt need to update the object full of refs
const fields = computed({
get: () => {
return {
per_page: ref(route.query.per_page || 20),
from: ref(route.query.from || undefined),
to: ref(route.query.to || undefined),
alert_severity: ref<AlertSeverity[]>(route.query.alert_severity as any[] || ['Low', 'Medium', 'High']),
alert_state: ref<AlertState[]>(route.query.alert_state as any[] || ['Triggered']),
include_dismissed: ref(route.query.include_dismissed === '1' || false)
}
},
set: (value: any) => {
router.push({
query: {
per_page: value.per_page,
from: value.from,
to: value.to,
alert_severity: value.alert_severity,
alert_state: value.alert_state,
include_dismissed: value.include_dismissed ? '1' : '0'
}
})
}
})
const fields = computed({
get: () => {
return {
per_page: ref(route.query.per_page || 20),
from: ref(route.query.from || undefined),
to: ref(route.query.to || undefined),
alert_severity: ref<AlertSeverity[]>(route.query.alert_severity as any[] || ['Low', 'Medium', 'High']),
alert_state: ref<AlertState[]>(route.query.alert_state as any[] || ['Triggered']),
include_dismissed: ref(route.query.include_dismissed === '1' || false)
}
},
set: (value: any) => {
router.push({
query: {
per_page: value.per_page,
from: value.from,
to: value.to,
alert_severity: value.alert_severity,
alert_state: value.alert_state,
include_dismissed: value.include_dismissed ? '1' : '0'
}
})
}
})
^ this is my iteration right now - may be missing something as I havent tested it yet i assume i dont need the refs as the computed is a ref in itself? reading it back seems super redundent to do refs inside that object seems like the setter isn't getting hit with the v-models ( also removed all the refs - redundent )
Gobbo
GobboOP8mo ago
No description
pyplacca
pyplacca8mo ago
You don’t need to wrap the values in a ref
Gobbo
GobboOP8mo ago
yeah got rid of them as realised computed is a ref 😭 only issue right now is set is never getting ran also this seems to make my ui elements sluggish? not sure why that'd be seems like it doesn't like the whole computed.field sort of deal, it doesn't register updates here and there, but sometimes it does
pyplacca
pyplacca8mo ago
hmm. that shouldn't affect the ux
Gobbo
GobboOP8mo ago
yeah it doesnt seem to be the UX more rather it seems to not be updating it all the time
pyplacca
pyplacca8mo ago
hmm. lemme check and see why
Gobbo
GobboOP8mo ago
here's me spam clicking the toggle
No description
Gobbo
GobboOP8mo ago
you can see it registers the update sometimes but doesnt others altho it seems like a ux thing because in some positions it just doesnt work, this is so lost on me haha
pyplacca
pyplacca8mo ago
possible to create reproduction so I check it out?
Gobbo
GobboOP8mo ago
so it only update when my mouse it out of view of the UX is what i've figured out - and how do you mean sorry
const fields = computed({
get: () => {
return {
per_page: route.query.per_page || 20,
from: route.query.from || undefined,
to: route.query.to || undefined,
alert_severity: route.query.alert_severity || ['Low', 'Medium', 'High'],
alert_state: route.query.alert_state || ['Triggered'],
include_dismissed: route.query.include_dismissed === '1' || false
}
},
set: (value: any) => {
console.log(value)

router.push({
query: {
per_page: value.per_page,
from: value.from,
to: value.to,
alert_severity: value.alert_severity,
alert_state: value.alert_state,
include_dismissed: value.include_dismissed ? '1' : '0'
}
})
}
})
const fields = computed({
get: () => {
return {
per_page: route.query.per_page || 20,
from: route.query.from || undefined,
to: route.query.to || undefined,
alert_severity: route.query.alert_severity || ['Low', 'Medium', 'High'],
alert_state: route.query.alert_state || ['Triggered'],
include_dismissed: route.query.include_dismissed === '1' || false
}
},
set: (value: any) => {
console.log(value)

router.push({
query: {
per_page: value.per_page,
from: value.from,
to: value.to,
alert_severity: value.alert_severity,
alert_state: value.alert_state,
include_dismissed: value.include_dismissed ? '1' : '0'
}
})
}
})
here's my computed - and as for my ui im using nuxt ui components <USelectMenu v-model="fields.alert_severity" :options="['Low', 'Medium', 'High']" multiple class="w-36" />
pyplacca
pyplacca8mo ago
I might be wrong but I'm thinking the component might be the cause
Gobbo
GobboOP8mo ago
yeah seems like it
No description
Gobbo
GobboOP8mo ago
only updates when i move my mouse away
pyplacca
pyplacca8mo ago
just tried with this sample and here's how it works on my end. no issues
const route = useRoute()
const state = computed({
get() {
const q = route.query as { mode?: Mode }
return {
mode: q.mode || 'buy',
}
},
set(v) {
navigateTo({
path: route.path,
query: v,
})
},
})
const route = useRoute()
const state = computed({
get() {
const q = route.query as { mode?: Mode }
return {
mode: q.mode || 'buy',
}
},
set(v) {
navigateTo({
path: route.path,
query: v,
})
},
})
pyplacca
pyplacca8mo ago
like a copy of it on Stackblitz
Gobbo
GobboOP8mo ago
issue is this is a massive repo of work and also it's my work work so wouldnt be allowed, as much as Id want to :( yeah this is bizarre, i meant my set isn't even running
pyplacca
pyplacca8mo ago
doesn't have to be exact. just a piece of it
Gobbo
GobboOP8mo ago
only pieces ive got are the ones above, the computed, then any component using said computed via v-model using nuxt ui components
pyplacca
pyplacca8mo ago
I guess you were right. It's doesn't work exactly as expected. Not sure why though try this alternative
const fields = reactive({
per_page: route.query.per_page || 20,
from: route.query.from || undefined,
to: route.query.to || undefined,
alert_severity: route.query.alert_severity || ['Low', 'Medium', 'High'],
alert_state: route.query.alert_state || ['Triggered'],
include_dismissed: route.query.include_dismissed === '1' || false,
})

watch(
fields,
(value) => {
console.log(value)
navigateTo({
query: {
per_page: value.per_page,
from: value.from,
to: value.to,
alert_severity: value.alert_severity,
alert_state: value.alert_state,
include_dismissed: value.include_dismissed ? '1' : '0',
},
replace: true,
})
},
{ deep: true },
)
const fields = reactive({
per_page: route.query.per_page || 20,
from: route.query.from || undefined,
to: route.query.to || undefined,
alert_severity: route.query.alert_severity || ['Low', 'Medium', 'High'],
alert_state: route.query.alert_state || ['Triggered'],
include_dismissed: route.query.include_dismissed === '1' || false,
})

watch(
fields,
(value) => {
console.log(value)
navigateTo({
query: {
per_page: value.per_page,
from: value.from,
to: value.to,
alert_severity: value.alert_severity,
alert_state: value.alert_state,
include_dismissed: value.include_dismissed ? '1' : '0',
},
replace: true,
})
},
{ deep: true },
)
@Gobbo
Gobbo
GobboOP8mo ago
will give it a shot :)
pyplacca
pyplacca8mo ago
alright
Gobbo
GobboOP8mo ago
works like a charm only issue is with arrays, so with dropdowns if you only have one option selected, the query params seems like a string rather than array as its only showing one option in the url - thus breaking the dropdown entirely ( errors ) not sure if theres a way to be more verbose about arrays in the query params like introducing [] in front, do you know of anyway to tell nuxt that or?
pyplacca
pyplacca8mo ago
nuxt router handles that very well
No description
pyplacca
pyplacca8mo ago
?per_page=20&alert_severity=Low&alert_severity=Medium&alert_severity=High&alert_state=Triggered&include_dismissed=false
Gobbo
GobboOP8mo ago
per_page=20&alert_severity=High&alert_state=Triggered&include_dismissed=0 issue is here - it doesn't like single values, maybe because it thinks its a string? ill see if i can be verbose about the type
Gobbo
GobboOP8mo ago
doesnt seem to like it
No description
pyplacca
pyplacca8mo ago
oh I get what you mean. you'd then have to transform the value
Gobbo
GobboOP8mo ago
yeah going to try just manually transform it seems just wrapping it in [] did the trick :) alert_severity: [route.query.alert_severity] || ['Low', 'Medium', 'High'], not sure i its bad practice or not, but it works appreciate all the help boss :) not sure if theres a way to make this reusable in some form of composable but ill mess around
pyplacca
pyplacca8mo ago
great. I don't think it's bad practice, but you could as the nuxt team in the general channel
Gobbo
GobboOP8mo ago
nevermind its bad practice, as it wraps the initial array ( query params with multiple values ) in another array :) ill mess around thanks for all the help
pyplacca
pyplacca8mo ago
anytime
pyplacca
pyplacca8mo ago
you could try something like this if you want
const fields = reactive({
...
alert_severity: (
(route.query.alert_severity as string) || 'Low,Medium,High'
).split(','),
// or this 👇. whichever best suits your code style
// alert_severity: (
// (route.query.alert_severity as string) ||
// ['Low', 'Medium', 'High'].join(',')
// ).split(','),
...
})

watch(
fields,
(value) => {
console.log(value)
navigateTo({
query: {
...
alert_severity: value.alert_severity.join(','),
...
},
replace: true,
})
},
{ deep: true },
)
const fields = reactive({
...
alert_severity: (
(route.query.alert_severity as string) || 'Low,Medium,High'
).split(','),
// or this 👇. whichever best suits your code style
// alert_severity: (
// (route.query.alert_severity as string) ||
// ['Low', 'Medium', 'High'].join(',')
// ).split(','),
...
})

watch(
fields,
(value) => {
console.log(value)
navigateTo({
query: {
...
alert_severity: value.alert_severity.join(','),
...
},
replace: true,
})
},
{ deep: true },
)
Gobbo
GobboOP8mo ago
could work, would also work if i transformed it back into an array too :) good idea
pyplacca
pyplacca8mo ago
yeah. the transformation is happening in both directions. ie; string-to-array in the reactive state and array-to-string in the watcher
Gobbo
GobboOP8mo ago
yeah thats what i opted for, read ur code wrong for a sec but got it
pyplacca
pyplacca8mo ago
no probs. great u figured it out
Gobbo
GobboOP8mo ago
appreciate it :) not sure if i can turn this into a composable without h eaving typescript work - would need some hard dynamic typing and dynamic watchers etc for another time this is short enough for me
pyplacca
pyplacca8mo ago
cool. I'll let you know if I come up with something no promises
Want results from more Discord servers?
Add your server