mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 16:31:11 +00:00
Implement navigate and set-query functions
This commit is contained in:
parent
f78116e346
commit
48bbdba8ed
5 changed files with 167 additions and 46 deletions
|
|
@ -12,7 +12,7 @@
|
|||
(vec vs))))
|
||||
|
||||
(defn query-params
|
||||
"Given goog.Uri, read query parameters into Clojure map."
|
||||
"Given goog.Uri, read query parameters into a Clojure map."
|
||||
[^Uri uri]
|
||||
(let [q (.getQueryData uri)]
|
||||
(->> q
|
||||
|
|
@ -20,6 +20,19 @@
|
|||
(map (juxt keyword #(query-param q %)))
|
||||
(into {}))))
|
||||
|
||||
(defn set-query-params
|
||||
"Given Reitit-frontend path, update the query params
|
||||
with given function and arguments.
|
||||
|
||||
Note: coercion is not applied to the query params"
|
||||
[path new-query-or-update-fn]
|
||||
(let [^goog.Uri uri (Uri/parse path)
|
||||
new-query (if (fn? new-query-or-update-fn)
|
||||
(new-query-or-update-fn (query-params uri))
|
||||
new-query-or-update-fn)]
|
||||
(.setQueryData uri (QueryData/createFromMap (clj->js new-query)))
|
||||
(.toString uri)))
|
||||
|
||||
(defn match-by-path
|
||||
"Given routing tree and current path, return match with possibly
|
||||
coerced parameters. Return nil if no match found.
|
||||
|
|
@ -88,14 +101,3 @@
|
|||
match)
|
||||
(do (js/console.warn "missing route" name)
|
||||
nil))))
|
||||
|
||||
(defn update-path-query-params
|
||||
"Given Reitit-frontend path, update the query params
|
||||
with given function and arguments.
|
||||
|
||||
NOTE: coercion is not applied to the query params"
|
||||
[path f & args]
|
||||
(let [^goog.Uri uri (Uri/parse path)
|
||||
new-query (apply f (query-params uri) args)]
|
||||
(.setQueryData uri (QueryData/createFromMap (clj->js new-query)))
|
||||
(.toString uri)))
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
"Easy wrapper over reitit.frontend.history,
|
||||
handling the state. Only one router can be active
|
||||
at a time."
|
||||
(:require [reitit.frontend.history :as rfh]
|
||||
[reitit.frontend :as rf]))
|
||||
(:require [reitit.frontend.history :as rfh]))
|
||||
|
||||
(defonce history (atom nil))
|
||||
|
||||
|
|
@ -103,14 +102,43 @@
|
|||
([name path-params query-params]
|
||||
(rfh/replace-state @history name path-params query-params)))
|
||||
|
||||
(defn update-query
|
||||
;; TODO: Sync the docstring with other namespaces
|
||||
"Takes the current location and updates the query params
|
||||
with given fn and arguments."
|
||||
[f & args]
|
||||
;; TODO: rfh version?
|
||||
(let [current-path (rfh/-get-path @history)
|
||||
new-path (apply rf/update-path-query-params current-path f args)]
|
||||
;; TODO: replaceState version
|
||||
(.pushState js/window.history nil "" new-path)
|
||||
(rfh/-on-navigate @history new-path)))
|
||||
;; This duplicates previous two, but the map parameter will be easier way to
|
||||
;; extend the functions, e.g. to work with fragment string. Toggling push vs
|
||||
;; replace can be also simpler with a flag.
|
||||
;; Navigate and set-query are also similer to react-router API.
|
||||
(defn
|
||||
^{:see-also ["reitit.frontend.history/navigate"]}
|
||||
navigate
|
||||
"Updates the browser location and either pushes new entry to the history stack
|
||||
or replaces the latest entry in the the history stack (controlled by
|
||||
`replace` option) using URL built from a route defined by name given
|
||||
parameters.
|
||||
|
||||
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.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState"
|
||||
([name]
|
||||
(rfh/navigate @history name))
|
||||
([name {:keys [path-params query-params replace] :as opts}]
|
||||
(rfh/navigate @history name opts)))
|
||||
|
||||
(defn
|
||||
^{:see-also ["reitit.frontend.history/set-query"]}
|
||||
set-query
|
||||
"Update query parameters for the current route.
|
||||
|
||||
New query params can be given as a map, or a function taking
|
||||
the old params and returning the new modified params.
|
||||
|
||||
Note: The query parameter values aren't coereced, so the
|
||||
update fn will see string values for all query params."
|
||||
([new-query-or-update-fn]
|
||||
(rfh/set-query @history new-query-or-update-fn))
|
||||
([new-query-or-update-fn {:keys [replace] :as opts}]
|
||||
(rfh/set-query @history new-query-or-update-fn opts)))
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
(defprotocol History
|
||||
(-init [this] "Create event listeners")
|
||||
(-stop [this] "Remove event listeners")
|
||||
(-on-navigate [this path])
|
||||
(-get-path [this])
|
||||
(-href [this path]))
|
||||
(-on-navigate [this path] "Find a match for current routing path and call on-navigate callback")
|
||||
(-get-path [this] "Get the current routing path")
|
||||
(-href [this path] "Converts given routing path to browser location"))
|
||||
|
||||
;; This version listens for both pop-state and hash-change for
|
||||
;; compatibility for old browsers not supporting History API.
|
||||
|
|
@ -177,7 +177,9 @@
|
|||
(if history
|
||||
(-stop history)))
|
||||
|
||||
(defn href
|
||||
(defn
|
||||
^{:see-also ["reitit.core/match->path"]}
|
||||
href
|
||||
"Generate a URL for a route defined by name, with given path-params and query-params.
|
||||
|
||||
The URL is formatted using Reitit frontend history handler, so using it with
|
||||
|
|
@ -219,7 +221,9 @@
|
|||
(.pushState js/window.history nil "" (-href history path))
|
||||
(-on-navigate history path))))
|
||||
|
||||
(defn replace-state
|
||||
(defn
|
||||
^{:see-also ["reitit.core/match->path"]}
|
||||
replace-state
|
||||
"Updates the browser location and replaces latest entry in the history stack
|
||||
using URL built from a route defined by name, with given path-params and
|
||||
query-params.
|
||||
|
|
@ -241,3 +245,50 @@
|
|||
path (reitit/match->path match query-params)]
|
||||
(.replaceState js/window.history nil "" (-href history path))
|
||||
(-on-navigate history path))))
|
||||
|
||||
(defn
|
||||
^{:see-also ["reitit.core/match->path"]}
|
||||
navigate
|
||||
"Updates the browser location and either pushes new entry to the history stack
|
||||
or replaces the latest entry in the the history stack (controlled by
|
||||
`replace` option) using URL built from a route defined by name given
|
||||
parameters.
|
||||
|
||||
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.
|
||||
|
||||
See also:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState"
|
||||
([history name]
|
||||
(navigate history name nil))
|
||||
([history name {:keys [path-params query-params replace] :as opts}]
|
||||
(let [match (rf/match-by-name! (:router history) name path-params)
|
||||
path (reitit/match->path match query-params)]
|
||||
(if replace
|
||||
(.replaceState js/window.history nil "" (-href history path))
|
||||
(.pushState js/window.history nil "" (-href history path)))
|
||||
(-on-navigate history path))))
|
||||
|
||||
(defn
|
||||
^{:see-also ["reitit.frontend/set-query-params"]}
|
||||
set-query
|
||||
"Update query parameters for the current route.
|
||||
|
||||
New query params can be given as a map, or a function taking
|
||||
the old params and returning the new modified params.
|
||||
|
||||
Note: The query parameter values aren't coereced, so the
|
||||
update fn will see string values for all query params."
|
||||
([history new-query-or-update-fn]
|
||||
(set-query history new-query-or-update-fn nil))
|
||||
([history new-query-or-update-fn {:keys [replace] :as opts}]
|
||||
(let [current-path (-get-path history)
|
||||
new-path (rf/set-query-params current-path new-query-or-update-fn)]
|
||||
(if replace
|
||||
(.replaceState js/window.history nil "" (-href history new-path))
|
||||
(.pushState js/window.history nil "" (-href history new-path)))
|
||||
(-on-navigate history new-path))))
|
||||
|
|
|
|||
|
|
@ -8,6 +8,19 @@
|
|||
[reitit.coercion.malli :as rcm]
|
||||
[reitit.frontend.test-utils :refer [capture-console]]))
|
||||
|
||||
(deftest query-params-test
|
||||
(is (= {:foo "1"}
|
||||
(rf/query-params (.parse goog.Uri "?foo=1"))))
|
||||
|
||||
(is (= {:foo "1" :bar "aaa"}
|
||||
(rf/query-params (.parse goog.Uri "?foo=1&bar=aaa"))))
|
||||
|
||||
(is (= {:foo ""}
|
||||
(rf/query-params (.parse goog.Uri "?foo="))))
|
||||
|
||||
(is (= {:foo ""}
|
||||
(rf/query-params (.parse goog.Uri "?foo")))))
|
||||
|
||||
(defn m [x]
|
||||
(assoc x :data nil :result nil))
|
||||
|
||||
|
|
@ -228,23 +241,30 @@
|
|||
:expires_in 3600}}})
|
||||
(m (rf/match-by-path router "/5?mode=foo#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600"))))))))
|
||||
|
||||
(deftest update-path-query-params-test
|
||||
(deftest set-query-params-test
|
||||
(is (= "foo?bar=1"
|
||||
(rf/update-path-query-params "foo" assoc :bar 1)))
|
||||
(rf/set-query-params "foo" {:bar 1})
|
||||
(rf/set-query-params "foo" #(assoc % :bar 1))))
|
||||
|
||||
(testing "Keep fragment"
|
||||
(is (= "foo?bar=1&zzz=2#aaa"
|
||||
(rf/update-path-query-params "foo?bar=1#aaa" assoc :zzz 2))))
|
||||
(rf/set-query-params "foo?bar=1#aaa" #(assoc % :zzz 2)))))
|
||||
|
||||
(is (= "foo?asd=1&bar=1"
|
||||
(rf/update-path-query-params "foo?asd=1" assoc :bar 1)))
|
||||
(rf/set-query-params "foo?asd=1" #(assoc % :bar 1))))
|
||||
|
||||
(is (= "foo?bar=1"
|
||||
(rf/update-path-query-params "foo?asd=1&bar=1" dissoc :asd)))
|
||||
(rf/set-query-params "foo?asd=1&bar=1" #(dissoc % :asd))))
|
||||
|
||||
(is (= "foo?bar"
|
||||
(rf/set-query-params "foo?asd=1&bar" #(dissoc % :asd))))
|
||||
|
||||
(is (= "foo?bar"
|
||||
(rf/set-query-params "foo" #(assoc % :bar ""))))
|
||||
|
||||
(is (= "foo"
|
||||
(rf/update-path-query-params "foo?asd=1" dissoc :asd)))
|
||||
(rf/set-query-params "foo?asd=1" #(dissoc % :asd))))
|
||||
|
||||
(testing "Need to coerce current values manually"
|
||||
(is (= "foo?foo=2"
|
||||
(rf/update-path-query-params "foo?foo=1" update :foo #(inc (js/parseInt %)))))))
|
||||
(rf/set-query-params "foo?foo=1" (fn [q] (update q :foo #(inc (js/parseInt %)))))))))
|
||||
|
|
|
|||
|
|
@ -30,33 +30,53 @@
|
|||
(is (= "/" url)
|
||||
"start at root")
|
||||
(rfe/push-state ::foo))
|
||||
;; 0. /
|
||||
;; 1. /foo
|
||||
2 (do (is (= "/foo" url)
|
||||
"push-state")
|
||||
(.back js/window.history))
|
||||
;; 0. /
|
||||
3 (do (is (= "/" url)
|
||||
"go back")
|
||||
(rfe/push-state ::bar {:id 1}))
|
||||
(rfe/navigate ::bar {:path-params {:id 1}}))
|
||||
;; 0. /
|
||||
;; 1. /bar/1
|
||||
4 (do (is (= "/bar/1" url)
|
||||
"push-state 2")
|
||||
(rfe/replace-state ::bar {:id 2}))
|
||||
;; 0. /
|
||||
;; 1. /bar/2
|
||||
5 (do (is (= "/bar/2" url)
|
||||
"replace-state")
|
||||
(.back js/window.history))
|
||||
6 (do (is (= "/" url)
|
||||
"go back after replace state")
|
||||
(rfe/set-query {:a 1}))
|
||||
;; 0. /
|
||||
;; 1. /bar/2
|
||||
;; 2. /bar/2?a=1
|
||||
6 (do (is (= "/bar/2?a=1" url)
|
||||
"update-query with map")
|
||||
(rfe/set-query #(assoc % :b "foo") {:replace true}))
|
||||
;; 0. /
|
||||
;; 1. /bar/2
|
||||
;; 2. /bar/2?a=1&b=foo
|
||||
7 (do (is (= "/bar/2?a=1&b=foo" url)
|
||||
"update-query with fn")
|
||||
(.go js/window.history -2))
|
||||
;; 0. /
|
||||
8 (do (is (= "/" url)
|
||||
"go back two events")
|
||||
|
||||
;; Reset to ensure old event listeners aren't called
|
||||
(rfe/start! router
|
||||
(fn on-navigate [match history]
|
||||
(let [url (rfh/-get-path history)]
|
||||
(case (swap! n inc)
|
||||
7 (do (is (= "/" url)
|
||||
9 (do (is (= "/" url)
|
||||
"start at root")
|
||||
(rfe/push-state ::foo))
|
||||
8 (do (is (= "/foo" url)
|
||||
"push-state")
|
||||
(rfh/stop! @rfe/history)
|
||||
(done))
|
||||
10 (do (is (= "/foo" url)
|
||||
"push-state")
|
||||
(rfh/stop! @rfe/history)
|
||||
(done))
|
||||
(do
|
||||
(is false (str "extra event 2" {:n @n, :url url}))
|
||||
(done)))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue