feat: add useRouteQuery composable

This commit is contained in:
Maxime Pauvert 2025-01-15 13:55:13 +01:00
parent 8136954e7c
commit 60ba57134c
No known key found for this signature in database
2 changed files with 173 additions and 2 deletions

View File

@ -0,0 +1,134 @@
---
title: "useRouteQuery"
description: The useRouteQuery composable simplifies working with query parameters.
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts
size: xs
---
::note
You can use `useRouteQuery` to seamlessly retrieve and update query parameters directly within your application.
::
## Example
In the following example, we create a dynamic search interface. The input field updates the `q` query parameter in the URL, which is then used to fetch data from the Google Books API.
```vue [~/pages/index.vue]
<script setup lang="ts">
const query = useRouteQuery('q');
const { data } = await useFetch('https://www.googleapis.com/books/v1/volumes', {
params: {
q: query,
},
});
</script>
<template>
<input v-model="query" placeholder="Search for books..." />
<div v-for="book in data?.items" :key="book.id">
{{ book.volumeInfo.title }}
</div>
</template>
```
## Features
- **Retrieve Query Parameters**: Access specific query parameters with default values.
- **Update Query Parameters**: Dynamically modify query parameters, reflecting changes in the URL.
- **Automatic Cleanup**: Remove empty or default parameters with configurable options.
## API
### `useRouteQuery(param, defaultValue, options)`
#### Parameters
- `param` (String): The name of the query parameter to manage.
- `defaultValue` (String): The default value if the query parameter is not present.
- `options` (Object):
- `removeEmpty` (Boolean): Remove parameters with empty values. Default: `true`.
- `removeDefault` (Boolean): Remove parameters equal to the default value. Default: `true`.
#### Returns
A `computed` object with:
- **`get`**: Retrieves the current value of the query parameter or the default value.
- **`set`**: Updates the query parameter in the URL.
## Additional Examples
### Basic Retrieval
Retrieve a query parameter with a default value fallback:
```javascript
const searchQuery = useRouteQuery('search', 'defaultSearch');
console.log(searchQuery.value); // Outputs: 'defaultSearch' if `?search` is not in the URL
```
### Dynamic Updates
Update the query parameter dynamically:
```javascript
const searchQuery = useRouteQuery('search', 'defaultSearch');
searchQuery.value = 'newSearchValue';
// URL updates to include `?search=newSearchValue`
searchQuery.value = '';
// URL removes `?search` due to `removeEmpty: true`
```
### Custom Options
Customize behavior with options:
```javascript
const filterQuery = useRouteQuery('filter', 'all', {
removeEmpty: false,
removeDefault: false,
});
filterQuery.value = '';
// URL remains `?filter=` because `removeEmpty` is `false`
filterQuery.value = 'all';
// URL remains `?filter=all` because `removeDefault` is `false`
```
### Toggle Query Parameter
Manage a toggle state with a query parameter:
```javascript
const isVisible = useRouteQuery('visible', false);
function toggleVisibility() {
isVisible.value = !isVisible.value;
}
// Updates the URL to `?visible=true` or removes `?visible`
```
### Managing Multiple Query Parameters
Handle multiple query parameters simultaneously:
```javascript
const category = useRouteQuery('category', 'all');
const sort = useRouteQuery('sort', 'asc');
category.value = 'books';
sort.value = 'desc';
// URL updates to `?category=books&sort=desc`
```
## Notes
- Ensure that `useRouteQuery` is used within a Nuxt component or setup function to access `useRoute` and `useRouter`.
- Query updates replace the entire query object. Ensure no unintended parameters are overwritten.
## Limitations
- This composable does not handle deeply nested query parameters.
- It is designed for flat query structures only.
::read-more{icon="i-simple-icons-vuedotjs" to="https://vuejs.org/api/reactivity-core.html#computed"}

View File

@ -1,5 +1,5 @@
import { getCurrentInstance, hasInjectionContext, inject, onScopeDispose } from 'vue'
import type { Ref } from 'vue'
import { computed, getCurrentInstance, hasInjectionContext, inject, onScopeDispose } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from 'vue-router'
import { sanitizeStatusCode } from 'h3'
import { hasProtocol, isScriptProtocol, joinURL, withQuery } from 'ufo'
@ -280,3 +280,40 @@ export function encodeURL (location: string, isExternalHost = false) {
}
return url.toString()
}
interface UseRouteQueryOptions {
removeEmpty?: boolean
removeDefault?: boolean
}
export function useRouteQuery (
param: string,
defaultValue: string | null = null,
options: UseRouteQueryOptions = {
removeEmpty: true,
removeDefault: true,
},
): ComputedRef<string | null> {
const route = useRoute()
const router = useRouter()
return computed<string | null>({
get () {
const value = route.query[param]
return (value as string | undefined) ?? defaultValue
},
set (value: string | null) {
const newQuery = { ...route.query, [param]: value }
if (options.removeEmpty && (value === null || value === undefined || value === '')) {
delete newQuery[param]
}
if (options.removeDefault && value === defaultValue) {
delete newQuery[param]
}
router.push({ query: newQuery })
},
})
}