diff --git a/doc/frontend/basics.md b/doc/frontend/basics.md index 6c049075..bdc18410 100644 --- a/doc/frontend/basics.md +++ b/doc/frontend/basics.md @@ -8,6 +8,10 @@ history events - Stateful wrapper for easy use of history integration - Optional [controller extension](./controllers.md) +You likely won't use `reitit.frontend` directly in your apps and instead you +will use the API documented in the browser integration docs, which wraps these +lower level functions. + ## Core functions `reitit.frontend` provides some useful functions wrapping core functions: @@ -23,7 +27,8 @@ enabled. `match-by-name` and `match-by-name!` with optional `path-paramers` and logging errors to `console.warn` instead of throwing errors to prevent -React breaking due to errors. +React breaking due to errors. These can also [encode query-parameters](./coercion.md) +using schema from match data. ## Next diff --git a/doc/frontend/browser.md b/doc/frontend/browser.md index 1541265b..4024fe26 100644 --- a/doc/frontend/browser.md +++ b/doc/frontend/browser.md @@ -13,6 +13,9 @@ There are also secondary functions following HTML5 History API: `push-state` to navigate to new route adding entry to the history and `replace-state` to change route without leaving previous entry in browser history. +See [coercion notes](./coercion.md) to see how frontend route parameters +can be decoded and encoded. + ## Fragment router Fragment is simple integration which stores the current route in URL fragment, @@ -62,7 +65,7 @@ event handler for page change events. ## History manipulation -Reitit doesn't include functions to manipulate the history stack, i.e. +Reitit doesn't include functions to manipulate the history stack, i.e., go back or forwards, but calling History API functions directly should work: ``` diff --git a/doc/frontend/coercion.md b/doc/frontend/coercion.md new file mode 100644 index 00000000..a58e57f5 --- /dev/null +++ b/doc/frontend/coercion.md @@ -0,0 +1,59 @@ +# Frontend coercion + +The Reitit frontend leverages [coercion](../coercion/coercion.md) for path, +query, and fragment parameters. The coercion uses the input schema defined +in the match data under `:parameters`. + +## Behavior of Coercion + +1. **Route Matching** + When matching a route from a path, the resulting match will include the + coerced values (if coercion is enabled) under `:parameters`. If coercion is + disabled, the parsed string values are stored in the same location. + The original un-coerced values are always available under `:path-params`, + `:query-params`, and `:fragment` (a single string). + +2. **Creating Links and Navigating** + When generating a URL (`href`) or navigating (`push-state`, `replace-state`, `navigate`) + to a route, coercion can be + used to encode query-parameter values into strings. This happens before + Reitit performs basic URL encoding on the values. This feature is + especially useful for handling the encoding of specific types, such as + keywords or dates, into strings. + +3. **Updating current query parameters** + When using `set-query` to modify current query parameters, Reitit frontend + first tries to find a match for the current path so the match can be used to + first decode query parameters and then to encode them. If the current path + doesn't match the routing tree, `set-query` keeps all the query parameter + values as strings. + +## Notes + +- **Value Encoding Support**: Only Malli supports value encoding. +- **Limitations**: Path parameters and fragment values are not encoded using + the match schema. + +## Example + +```cljs +(def router (r/router ["/" + ["" ::frontpage] + ["bar" + {:name ::bar + :coercion rcm/coercion + :parameters {:query [:map + [:q {:optional true} + [:keyword + {:decode/string (fn [s] (keyword (subs s 2))) + :encode/string (fn [k] (str "__" (name k)))}]]]}}]])) + +(rfe/href ::bar {} {:q :hello}) +;; Result "/bar?q=__hello", the :q value is first encoded + +(rfe/push-state ::bar {} {:q :world}) +;; Result "/bar?q=__world" +;; The current match will contain both the original value and parsed & decoded parameters: +;; {:query-params {:q "__world"} +;; :parameters {:query {:q :world}}} +``` diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index e9042772..adb446f9 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -187,9 +187,10 @@ The URL is formatted using Reitit frontend history handler, so using it with anchor element href will correctly trigger route change event. - Note: currently collections in query parameters are encoded as field-value - pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them - differently, convert the collections to strings first." + By default currently collections in query parameters are encoded as field-value + pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can + either use Malli coercion to encode values, or just turn the values to strings + before calling the function." ([history name] (href history name nil)) ([history name path-params] @@ -208,9 +209,10 @@ Will also trigger on-navigate callback on Reitit frontend History handler. - Note: currently collections in query-parameters are encoded as field-value - pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them - differently, convert the collections to strings first. + By default currently collections in query parameters are encoded as field-value + pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can + either use Malli coercion to encode values, or just turn the values to strings + before calling the function. See also: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState" @@ -236,9 +238,10 @@ Will also trigger on-navigate callback on Reitit frontend History handler. - Note: currently collections in query-parameters are encoded as field-value - pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them - differently, convert the collections to strings first. + By default currently collections in query parameters are encoded as field-value + pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can + either use Malli coercion to encode values, or just turn the values to strings + before calling the function. See also: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState" @@ -264,9 +267,10 @@ Will also trigger on-navigate callback on Reitit frontend History handler. - Note: currently collections in query-parameters are encoded as field-value - pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them - differently, convert the collections to strings first. + By default currently collections in query parameters are encoded as field-value + pairs separated by &, i.e. \"?a=1&a=2\". To encode them differently, you can + either use Malli coercion to encode values, or just turn the values to strings + before calling the function. See also: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState