Controllers run code when a route is entered and left. This can be useful to:
A controller consists of three functions:
params
which takes a Match and returns an arbitrary value.start
which takes the result of params and whose return value is discarded.stop
which takes the result of params and whose return value is discarded.When you navigate to a route that has a controller, params
gets called first
and then start
is called with its return value. When you exit that route,
stop
is called with the return value of params.
If you navigate to the same route with different parameters, params
gets
called again. If the return value changes from the previous return value, stop
and start
get called again.
You can add controllers to a route by adding them to the route data in the
:controllers
vector. For example:
["/item/:id"
{:controllers [{:params (fn [match] (get-in match [:path-params :id]))
:start (fn [item-id] (js/console.log :start item-id))
:stop (fn [item-id] (js/console.log :stop item-id))}]}]
If you leave out params
, start
and stop
get called with nil
. You can
leave out start
or stop
if you do not need both of them.
You need to
call
reitit.frontend.controllers/apply-controllers
whenever
the URL changes. You can call it from the on-navigate
callback of
reitit.frontend.easy
:
(ns frontend.core
(:require [reitit.frontend.easy :as rfe]
[reitit.frontend.controllers :as rfc]))
(defonce match-a (atom nil))
(def routes
["/" ...])
(defn init! []
(rfe/start!
routes
(fn [new-match]
(swap! match-a
(fn [old-match]
(when new-match
(assoc new-match
:controllers (rfc/apply-controllers (:controllers old-match) new-match))))))))
See also the full example.
When you nest routes in the route tree, the controllers get nested as well. Consider this route tree:
["/" {:controllers [{:start (fn [_] (js/console.log "root start"))}]}
["/item/:id"
{:controllers [{:params (fn [match] (get-in match [:path-params :id]))
:start (fn [item-id] (js/console.log "item start" item-id))
:stop (fn [item-id] (js/console.log "item stop" item-id))}]}]]
/item/something
, the root controller gets started first
and then the item controller gets started./item/something
to /item/something-else
, first
the item controller gets stopped with parameter something
and then it gets
started with the parameter something-else
. The root controller stays on the
whole time since its parameters do not change.Controllers can be used to load resources from a server. If and when your API requires authentication you will need to implement logic to prevent controllers trying to do requests if user isn't authenticated yet.
If you have both unauthenticated and authenticated resources, you can run the controllers always and then check the authentication status on controller code, or on the code called from controllers (e.g. re-frame event handler).
If all your resources require authentication an easy way to prevent bad
requests is to enable controllers only after authentication is done.
To do this you can check authentication status and call apply-controllers
only after authentication is done (also remember to manually call apply-controllers
with current match
when authentication is done). Or if no navigation is possible
before authentication is done, you can start the router only after
authentication is done.
Similar solution could be used to describe required resources as data (maybe even GraphQL query) per route, and then have code automatically load missing resources.
Can you improve this documentation? These fine people already did:
Tommi Reiman, Juho Teperi & Miikka KoskinenEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close