diff --git a/examples/frontend-controllers/checkouts/reitit-core b/examples/frontend-controllers/checkouts/reitit-core
new file mode 120000
index 00000000..a59d247e
--- /dev/null
+++ b/examples/frontend-controllers/checkouts/reitit-core
@@ -0,0 +1 @@
+../../../modules/reitit-core
\ No newline at end of file
diff --git a/examples/frontend-controllers/checkouts/reitit-frontend b/examples/frontend-controllers/checkouts/reitit-frontend
new file mode 120000
index 00000000..20cdd448
--- /dev/null
+++ b/examples/frontend-controllers/checkouts/reitit-frontend
@@ -0,0 +1 @@
+../../../modules/reitit-frontend
\ No newline at end of file
diff --git a/examples/frontend-controllers/checkouts/reitit-schema b/examples/frontend-controllers/checkouts/reitit-schema
new file mode 120000
index 00000000..a68c7f05
--- /dev/null
+++ b/examples/frontend-controllers/checkouts/reitit-schema
@@ -0,0 +1 @@
+../../../modules/reitit-schema
\ No newline at end of file
diff --git a/examples/frontend-controllers/project.clj b/examples/frontend-controllers/project.clj
new file mode 100644
index 00000000..c040a046
--- /dev/null
+++ b/examples/frontend-controllers/project.clj
@@ -0,0 +1,52 @@
+(defproject frontend "0.1.0-SNAPSHOT"
+ :description "FIXME: write description"
+ :url "http://example.com/FIXME"
+ :license {:name "Eclipse Public License"
+ :url "http://www.eclipse.org/legal/epl-v10.html"}
+
+ :dependencies [[org.clojure/clojure "1.9.0"]
+ [ring-server "0.5.0"]
+ [reagent "0.8.1"]
+ [ring "1.6.3"]
+ [compojure "1.6.1"]
+ [hiccup "1.0.5"]
+ [org.clojure/clojurescript "1.10.339"]
+ [metosin/reitit "0.1.4-SNAPSHOT"]
+ [metosin/reitit-schema "0.1.4-SNAPSHOT"]
+ [metosin/reitit-frontend "0.1.4-SNAPSHOT"]
+ ;; Just for pretty printting the match
+ [fipp "0.6.12"]]
+
+ :plugins [[lein-cljsbuild "1.1.7"]
+ [lein-figwheel "0.5.16"]]
+
+ :source-paths []
+ :resource-paths ["resources" "target/cljsbuild"]
+
+ :profiles {:dev {:dependencies [[binaryage/devtools "0.9.10"]]}}
+
+ :cljsbuild
+ {:builds
+ [{:id "app"
+ :figwheel true
+ :source-paths ["src"]
+ :watch-paths ["src" "checkouts/reitit-frontend/src"]
+ :compiler {:main "frontend.core"
+ :asset-path "/js/out"
+ :output-to "target/cljsbuild/public/js/app.js"
+ :output-dir "target/cljsbuild/public/js/out"
+ :source-map true
+ :optimizations :none
+ :pretty-print true
+ :preloads [devtools.preload]}}
+ {:id "min"
+ :source-paths ["src"]
+ :compiler {:output-to "target/cljsbuild/public/js/app.js"
+ :output-dir "target/cljsbuild/public/js"
+ :source-map "target/cljsbuild/public/js/app.js.map"
+ :optimizations :advanced
+ :pretty-print false}}]}
+
+ :figwheel {:http-server-root "public"
+ :server-port 3449
+ :nrepl-port 7002})
diff --git a/examples/frontend-controllers/resources/public/index.html b/examples/frontend-controllers/resources/public/index.html
new file mode 100644
index 00000000..bbad514c
--- /dev/null
+++ b/examples/frontend-controllers/resources/public/index.html
@@ -0,0 +1,10 @@
+
+
+
+ Reitit frontend example
+
+
+
+
+
+
diff --git a/examples/frontend-controllers/src/frontend/core.cljs b/examples/frontend-controllers/src/frontend/core.cljs
new file mode 100644
index 00000000..960867a5
--- /dev/null
+++ b/examples/frontend-controllers/src/frontend/core.cljs
@@ -0,0 +1,95 @@
+(ns frontend.core
+ (:require [reagent.core :as r]
+ [reitit.core :as re]
+ [reitit.frontend :as rf]
+ [reitit.frontend.history :as rfh]
+ [reitit.frontend.controllers :as rfc]
+ [reitit.coercion :as rc]
+ [reitit.coercion.schema :as rsc]
+ [schema.core :as s]
+ [fipp.edn :as fedn]))
+
+(defonce history (atom nil))
+
+(defn home-page []
+ [:div
+ [:h2 "Welcome to frontend"]
+ [:p "Look at console log for controller calls."]])
+
+(defn item-page [match]
+ (let [{:keys [path query]} (:parameters match)
+ {:keys [id]} path]
+ [:div
+ [:ul
+ [:li [:a {:href (rfh/href @history ::item {:id 1})} "Item 1"]]
+ [:li [:a {:href (rfh/href @history ::item {:id 2} {:foo "bar"})} "Item 2"]]]
+ (if id
+ [:h2 "Selected item " id])
+ (if (:foo query)
+ [:p "Optional foo query param: " (:foo query)])]))
+
+(defonce match (r/atom nil))
+
+(defn current-page []
+ [:div
+ [:ul
+ [:li [:a {:href (rfh/href @history ::frontpage)} "Frontpage"]]
+ [:li
+ [:a {:href (rfh/href @history ::item-list)} "Item list"]
+ ]]
+ (if @match
+ (let [view (:view (:data @match))]
+ [view @match]))
+ [:pre (with-out-str (fedn/pprint @match))]])
+
+(defn log-fn [& params]
+ (fn [_]
+ (apply js/console.log params)))
+
+(def routes
+ (re/router
+ ["/"
+ [""
+ {:name ::frontpage
+ :view home-page
+ :controllers [{:start (log-fn "start" "frontpage controller")
+ :stop (log-fn "stop" "frontpage controller")}]}]
+ ["items"
+ ;; Shared data for sub-routes
+ {:view item-page
+ :controllers [{:start (log-fn "start" "items controller")
+ :stop (log-fn "stop" "items controller")}]}
+
+ [""
+ {:name ::item-list
+ :controllers [{:start (log-fn "start" "item-list controller")
+ :stop (log-fn "stop" "item-list controller")}]}]
+ ["/:id"
+ {:name ::item
+ :parameters {:path {:id s/Int}
+ :query {(s/optional-key :foo) s/Keyword}}
+ :controllers [{:params (fn [match]
+ (:path (:parameters match)))
+ :start (fn [params]
+ (js/console.log "start" "item controller" (:id params)))
+ :stop (fn [params]
+ (js/console.log "stop" "item controller" (:id params)))}]}]]]
+ {:compile rc/compile-request-coercers
+ :data {:controllers [{:start (log-fn "start" "root-controller")
+ :stop (log-fn "stop" "root controller")}]
+ :coercion rsc/coercion}}))
+
+(defn init! []
+ (swap! history (fn [old-history]
+ (rfh/stop! old-history)
+ (rfh/start!
+ routes
+ (fn [new-match]
+ (swap! match (fn [old-match]
+ (if new-match
+ (assoc new-match :controllers (rfc/apply-controllers (:controllers old-match) new-match))))))
+ {:use-fragment true
+ :path-prefix "/"})))
+ (r/render [current-page] (.getElementById js/document "app")))
+
+(init!)
diff --git a/examples/frontend/src/frontend/core.cljs b/examples/frontend/src/frontend/core.cljs
index 82731258..f8c4f890 100644
--- a/examples/frontend/src/frontend/core.cljs
+++ b/examples/frontend/src/frontend/core.cljs
@@ -8,7 +8,7 @@
[schema.core :as s]
[fipp.edn :as fedn]))
-(def router (atom nil))
+(defonce history (atom nil))
(defn home-page []
[:div
@@ -19,8 +19,8 @@
[:h2 "About frontend"]
[:ul
[:li [:a {:href "http://google.com"} "external link"]]
- [:li [:a {:href (rfh/href @router ::foobar)} "Missing route"]]
- [:li [:a {:href (rfh/href @router ::item)} "Missing route params"]]]])
+ [:li [:a {:href (rfh/href @history ::foobar)} "Missing route"]]
+ [:li [:a {:href (rfh/href @history ::item)} "Missing route params"]]]])
(defn item-page [match]
(let [{:keys [path query]} (:parameters match)
@@ -35,10 +35,10 @@
(defn current-page []
[:div
[:ul
- [:li [:a {:href (rfh/href @router ::frontpage)} "Frontpage"]]
- [:li [:a {:href (rfh/href @router ::about)} "About"]]
- [:li [:a {:href (rfh/href @router ::item {:id 1})} "Item 1"]]
- [:li [:a {:href (rfh/href @router ::item {:id 2} {:foo "bar"})} "Item 2"]]]
+ [:li [:a {:href (rfh/href @history ::frontpage)} "Frontpage"]]
+ [:li [:a {:href (rfh/href @history ::about)} "About"]]
+ [:li [:a {:href (rfh/href @history ::item {:id 1})} "Item 1"]]
+ [:li [:a {:href (rfh/href @history ::item {:id 2} {:foo "bar"})} "Item 2"]]]
(if @match
(let [view (:view (:data @match))]
[view @match]))
@@ -62,10 +62,12 @@
:data {:coercion rsc/coercion}}))
(defn init! []
- (reset! router (rfh/start! routes
- (fn [m] (reset! match m))
- {:use-fragment true
- :path-prefix "/"}))
+ (swap! history (fn [old-history]
+ (rfh/stop! old-history)
+ (rfh/start! routes
+ (fn [m] (reset! match m))
+ {:use-fragment true
+ :path-prefix "/"})))
(r/render [current-page] (.getElementById js/document "app")))
(init!)
diff --git a/modules/reitit-frontend/src/reitit/frontend/controllers.cljs b/modules/reitit-frontend/src/reitit/frontend/controllers.cljs
index fe97f675..8556314d 100644
--- a/modules/reitit-frontend/src/reitit/frontend/controllers.cljs
+++ b/modules/reitit-frontend/src/reitit/frontend/controllers.cljs
@@ -22,9 +22,9 @@
those previously enabled. Resets controllers whose
parameters have changed."
[old-controllers new-match]
- (let [new-controllers (map (fn [controller]
- (assoc controller ::params (get-params controller new-match)))
- (:controllers (:data new-match)))
+ (let [new-controllers (mapv (fn [controller]
+ (assoc controller ::params (get-params controller new-match)))
+ (:controllers (:data new-match)))
changed-controllers (->> (map (fn [old new]
;; different controllers, or params changed
(if (not= old new)
diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs
index 97c135b1..7e178723 100644
--- a/modules/reitit-frontend/src/reitit/frontend/history.cljs
+++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs
@@ -57,7 +57,11 @@
(.replaceToken history (path->token history (.getPath uri)))))))
(defn start!
- "Parameters:
+ "This registers event listeners on either haschange or HTML5 history.
+ When using with development workflow like Figwheel, rememeber to
+ remove listeners using stop! call before calling start! again.
+
+ Parameters:
- router The reitit routing tree.
- on-navigate Function to be called when route changes.
@@ -94,7 +98,7 @@
:close-fn (fn []
(e/unlistenByKey event-key)
(e/unlistenByKey click-listen-key)
- (.setEnabled history false))}))
+ (.dispose history))}))
(defn stop! [{:keys [close-fn]}]
(if close-fn