From 82f8aaa8cfc71cc37d5a1069f4ecdf2d46afb99e Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Wed, 11 Jul 2018 13:19:24 +0300 Subject: [PATCH] Add frontend-controllers example --- .../checkouts/reitit-core | 1 + .../checkouts/reitit-frontend | 1 + .../checkouts/reitit-schema | 1 + examples/frontend-controllers/project.clj | 52 ++++++++++ .../resources/public/index.html | 10 ++ .../src/frontend/core.cljs | 95 +++++++++++++++++++ examples/frontend/src/frontend/core.cljs | 24 ++--- .../src/reitit/frontend/controllers.cljs | 6 +- .../src/reitit/frontend/history.cljs | 8 +- 9 files changed, 182 insertions(+), 16 deletions(-) create mode 120000 examples/frontend-controllers/checkouts/reitit-core create mode 120000 examples/frontend-controllers/checkouts/reitit-frontend create mode 120000 examples/frontend-controllers/checkouts/reitit-schema create mode 100644 examples/frontend-controllers/project.clj create mode 100644 examples/frontend-controllers/resources/public/index.html create mode 100644 examples/frontend-controllers/src/frontend/core.cljs 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