This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-04-25
Channels
- # announcements (4)
- # babashka (3)
- # beginners (79)
- # biff (4)
- # calva (17)
- # cider (18)
- # clj-kondo (21)
- # cljdoc (45)
- # cljs-dev (14)
- # cljsrn (9)
- # clojure (90)
- # clojure-europe (86)
- # clojure-italy (3)
- # clojure-nl (3)
- # clojure-portugal (1)
- # clojure-uk (9)
- # clojurescript (20)
- # code-reviews (23)
- # conjure (14)
- # cursive (12)
- # datascript (12)
- # emacs (5)
- # events (2)
- # fulcro (13)
- # gratitude (1)
- # holy-lambda (9)
- # lambdaisland (2)
- # malli (6)
- # nbb (1)
- # nextjournal (2)
- # nrepl (30)
- # off-topic (63)
- # pathom (1)
- # portal (24)
- # reagent (5)
- # reitit (13)
- # releases (2)
- # remote-jobs (1)
- # sci (90)
- # shadow-cljs (59)
- # spacemacs (5)
- # sql (13)
- # testing (20)
- # tools-build (17)
- # xtdb (27)
Hi, from inside a mutation I want to pull rc/component-options
from the currently mounted, routed-to component. What is the direct or shortest way from app or state to the class of the current route? There is dr/current-route-class
which takes a this which I struggle to provide from inside a mutation.
The shortest one I found so far ist sm-env
(let [sm-env (uism/state-machine-env @state nil router-id :none {})
path-segment (uism/retrieve sm-env :path-segment)]
(dr/resolve-target app path-segment))
Is there something more direct I am overlooking?if you know the router-id the current route component is stored in app db, that's what that helper uses: https://github.com/fulcrologic/fulcro/blob/16f1ad6f63864454f0cb43702fc4cbcdb7e04fbb/src/main/com/fulcrologic/fulcro/routing/dynamic_routing.cljc#L224 so you could make a helper fn of app-state, router-id -> current component class
Let me clear up something about dynamic routers that is here by design, but does prove to be a bit of a pain: “current route” is not a “single” thing. This has been both a strength and weakness of the design from the beginning, current-route-class
is meant to be passed a router, and only gives the current target of that router.
You see, there can be more than one router active as siblings (think master/detail screen, where the thing on the left can change via a router, as can the thing on the right). Of course, the browser URL can’t support the idea that you’re in two places at once easily without some additional encoding, but browser URLs have no direct dependency relationship to the dynamic routing API. If you choose to make an app where only one ultimate leaf target is active at a time, then it is easy to match that to a URI system in a browser. But the API of dynamic routing was always designed to be more flexible than that.
Thus the problem with “current route”. There isn’t one, in a pure API sense, unless you guarantee there is only one target active on-screen at once. Now, since your rendering code determines what is actually visible, the dr
API cannot possibly know what the current route is (every router in the system has a current target, independent of if it is rendered).
Now, that isn’t completely true. IF you guarantee that you only ever put one thing on screen at a time, then it is possible to derive the current route in a few ways:
1. Use dr/current-route
, which relies on the above assumption and uses Fulcro’s on-screen mounted components index to find a target that is currently being rendered, and then map that route to it’s class. This is relatively flaky, but I put the function in there with the docstring warning because it was such a commonly requested thing (which makes sense).
2. Use the current dynamic query and current app state to search through the query and find the target(s) on-screen, building up “paths” to them as you go, then return those paths (or perhaps a more complete data structure, like path, component, and even on-screen instance). I’ve yet to write this, though it isn’t that terribly difficult. I just haven’t had the time. This has the advantage of also working well when there is more than one active “leaf”
3. Keep track of it via your own routing wrapper. This one is trivial to implement. Make an atom that tracks what you want, and then a wrapper function that records that. Use your wrapper for all routing operations.
If anyone wants to give a shot at a PR related to this, here’s a version of “active routes” that is close to right…I threw it together and do not have time to completely debug it. I know it works for some cases, but it isn’t quite right. This can be unit-tested against a headless app as it does not rely on UI at all, just the active queries.
(letfn [(active-routes* [state-map {:keys [path] :as result} ast-nodes]
(into #{}
(remove nil?
(mapcat
(fn [{:keys [component children]}]
(let [ident (some-> component (comp/get-ident {}))
router? (= (first ident) ::dr/id)
{:keys [route-segment]} (some-> component (comp/component-options))]
(cond
router?
(let [active-target-ident (get-in state-map (conj ident ::dr/current-route))
active-target-ast-node (first
(filter (fn [{:keys [dispatch-key]}]
(= ::dr/current-route dispatch-key))
children))
active-target (:component active-target-ast-node)
{:keys [route-segment]} (comp/component-options active-target)]
(active-routes* state-map {:path (into path route-segment)
:target-ident active-target-ident
:target-class active-target} (:children active-target-ast-node)))
(and route-segment (some #(= :join (:type %)) children))
(let [child-results (active-routes* state-map result children)]
child-results)
(seq path)
[result]
:else nil)))
ast-nodes))))]
(defn active-routes
"Return a sequence of the ultimate router targets that are active in the given app. This is accomplished
by reading the active dynamic query, and walking that query from the root towards the leaves. Thus,
if you use disconnected roots you must specify a starting component from which to scan, and will
get back paths relative to that `starting-from` (a component, element, or factory if you're using
factory-based dynamic queries)."
([app]
(active-routes app (app/app-root app)))
([app starting-from]
(let [state-map (app/current-state app)
query (comp/get-query starting-from state-map)
{:keys [children] :as ast} (eql/query->ast query)]
(active-routes* state-map {:path []} children)))))
@U0CKQ19AQ I have it in my look-into list, but I'm swamped at the moment.
Thanks!
Available in 3.5.19-SNAPSHOT, commit id b083a3a77e7c387aba78a9ce49cd7417f9c72e03.
dr/active-routes
It tests out according to the automated test here: https://github.com/fulcrologic/fulcro/blob/develop/src/test/com/fulcrologic/fulcro/routing/dynamic_routing_test.cljc#L201