mirror of
https://github.com/metosin/reitit.git
synced 2025-12-17 08:21:11 +00:00
Merge pull request #260 from vharmain/frontend-examples
Frontend examples
This commit is contained in:
commit
59d68d65ef
18 changed files with 676 additions and 1 deletions
13
examples/frontend-links/README.md
Normal file
13
examples/frontend-links/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# reitit-frontend example
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```clj
|
||||||
|
> lein figwheel
|
||||||
|
```
|
||||||
|
|
||||||
|
Go with browser to http://localhost:3449
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright © 2018 Metosin Oy
|
||||||
62
examples/frontend-links/project.clj
Normal file
62
examples/frontend-links/project.clj
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
(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.10.0"]
|
||||||
|
[ring-server "0.5.0"]
|
||||||
|
[reagent "0.8.1"]
|
||||||
|
[ring "1.7.1"]
|
||||||
|
[hiccup "1.0.5"]
|
||||||
|
[org.clojure/clojurescript "1.10.520"]
|
||||||
|
[metosin/reitit "0.3.1"]
|
||||||
|
[metosin/reitit-spec "0.3.1"]
|
||||||
|
[metosin/reitit-frontend "0.3.1"]
|
||||||
|
;; Just for pretty printting the match
|
||||||
|
[fipp "0.6.14"]]
|
||||||
|
|
||||||
|
:plugins [[lein-cljsbuild "1.1.7"]
|
||||||
|
[lein-figwheel "0.5.18"]
|
||||||
|
[cider/cider-nrepl "0.21.1"]]
|
||||||
|
|
||||||
|
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
|
||||||
|
|
||||||
|
:source-paths ["src"]
|
||||||
|
:resource-paths ["resources" "target/cljsbuild"]
|
||||||
|
|
||||||
|
:profiles
|
||||||
|
{:dev
|
||||||
|
{:dependencies
|
||||||
|
[[binaryage/devtools "0.9.10"]
|
||||||
|
[cider/piggieback "0.4.0"]
|
||||||
|
[figwheel-sidecar "0.5.18"]]}}
|
||||||
|
|
||||||
|
:cljsbuild
|
||||||
|
{:builds
|
||||||
|
[{:id "app"
|
||||||
|
:figwheel true
|
||||||
|
:source-paths ["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]
|
||||||
|
:aot-cache true}}
|
||||||
|
{: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
|
||||||
|
:aot-cache true}}]}
|
||||||
|
|
||||||
|
:figwheel {:http-server-root "public"
|
||||||
|
:server-port 3449
|
||||||
|
:nrepl-port 7002
|
||||||
|
;; Server index.html for all routes for HTML5 routing
|
||||||
|
:ring-handler backend.server/handler})
|
||||||
10
examples/frontend-links/resources/public/index.html
Normal file
10
examples/frontend-links/resources/public/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Reitit frontend example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="/js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
11
examples/frontend-links/src/backend/server.clj
Normal file
11
examples/frontend-links/src/backend/server.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
(ns backend.server
|
||||||
|
(:require [clojure.java.io :as io]
|
||||||
|
[ring.util.response :as resp]
|
||||||
|
[ring.middleware.content-type :as content-type]))
|
||||||
|
|
||||||
|
(def handler
|
||||||
|
(-> (fn [request]
|
||||||
|
(or (resp/resource-response (:uri request) {:root "public"})
|
||||||
|
(-> (resp/resource-response "index.html" {:root "public"})
|
||||||
|
(resp/content-type "text/html"))))
|
||||||
|
content-type/wrap-content-type))
|
||||||
147
examples/frontend-links/src/frontend/core.cljs
Normal file
147
examples/frontend-links/src/frontend/core.cljs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
(ns frontend.core
|
||||||
|
(:require [clojure.string :as string]
|
||||||
|
[fipp.edn :as fedn]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[reitit.coercion :as rc]
|
||||||
|
[reitit.coercion.spec :as rss]
|
||||||
|
[reitit.frontend :as rf]
|
||||||
|
[reitit.frontend.easy :as rfe]
|
||||||
|
[spec-tools.data-spec :as ds]))
|
||||||
|
|
||||||
|
;; Components similar to react-router `Link`, `NavLink` and `Redirect`
|
||||||
|
;; with Reitit frontend.
|
||||||
|
|
||||||
|
(defn home-page []
|
||||||
|
[:div
|
||||||
|
[:h2 "Welcome to frontend"]
|
||||||
|
[:p "This is home page"]])
|
||||||
|
|
||||||
|
(defn about-page []
|
||||||
|
[:div
|
||||||
|
[:h2 "About frontend"]
|
||||||
|
[:p "This is about page"]])
|
||||||
|
|
||||||
|
(defn redirect!
|
||||||
|
"If `push` is truthy, previous page will be left in history."
|
||||||
|
[{:keys [to path-params query-params push]}]
|
||||||
|
(if push
|
||||||
|
(rfe/push-state to path-params query-params)
|
||||||
|
(rfe/replace-state to path-params query-params)))
|
||||||
|
|
||||||
|
(defn Redirect
|
||||||
|
"Component that only causes a redirect side-effect."
|
||||||
|
[props]
|
||||||
|
(r/create-class
|
||||||
|
{:component-did-mount (fn [this] (redirect! (r/props this)))
|
||||||
|
:component-did-update (fn [this [_ prev-props]]
|
||||||
|
(if (not= (r/props this) prev-props)
|
||||||
|
(redirect! (r/props this))))
|
||||||
|
:render (fn [this] nil)}))
|
||||||
|
|
||||||
|
(defn item-page [match]
|
||||||
|
(let [{:keys [path query]} (:parameters match)
|
||||||
|
{:keys [id]} path]
|
||||||
|
(if (< id 1)
|
||||||
|
[Redirect {:to ::frontpage}]
|
||||||
|
[:div
|
||||||
|
[:h2 "Selected item " id]
|
||||||
|
(when (:foo query)
|
||||||
|
[:p "Optional foo query param: " (:foo query)])])))
|
||||||
|
|
||||||
|
(def routes
|
||||||
|
[["/"
|
||||||
|
{:name ::frontpage
|
||||||
|
:view home-page}]
|
||||||
|
|
||||||
|
["/about"
|
||||||
|
{:name ::about
|
||||||
|
:view about-page}]
|
||||||
|
|
||||||
|
["/item/:id"
|
||||||
|
{:name ::item
|
||||||
|
:view item-page
|
||||||
|
:parameters
|
||||||
|
{:path {:id int?}
|
||||||
|
:query {(ds/opt :foo) keyword?}}}]])
|
||||||
|
|
||||||
|
(def router
|
||||||
|
(rf/router routes {:data {:coercion rss/coercion}}))
|
||||||
|
|
||||||
|
(defonce current-match (r/atom nil))
|
||||||
|
|
||||||
|
(defn- resolve-href
|
||||||
|
[to path-params query-params]
|
||||||
|
(if (keyword? to)
|
||||||
|
(rfe/href to path-params query-params)
|
||||||
|
(let [match (rf/match-by-path router to)
|
||||||
|
route (-> match :data :name)
|
||||||
|
params (or path-params (:path-params match))
|
||||||
|
query (or query-params (:query-params match))]
|
||||||
|
(if match
|
||||||
|
(rfe/href route params query)
|
||||||
|
to))))
|
||||||
|
|
||||||
|
(defn Link
|
||||||
|
[{:keys [to path-params query-params active]} & children]
|
||||||
|
(let [href (resolve-href to path-params query-params)]
|
||||||
|
(into
|
||||||
|
[:a {:href href} (when active "> ")] ;; Apply styles or whatever
|
||||||
|
children)))
|
||||||
|
|
||||||
|
(defn- name-matches?
|
||||||
|
[name path-params match]
|
||||||
|
(and (= name (-> match :data :name))
|
||||||
|
(= (not-empty path-params)
|
||||||
|
(-> match :parameters :path not-empty))))
|
||||||
|
|
||||||
|
(defn- url-matches?
|
||||||
|
[url match]
|
||||||
|
(= (-> url (string/split #"\?") first)
|
||||||
|
(:path match)))
|
||||||
|
|
||||||
|
(defn NavLink
|
||||||
|
[{:keys [to path-params] :as props} & children]
|
||||||
|
(let [active (or (name-matches? to path-params @current-match)
|
||||||
|
(url-matches? to @current-match))]
|
||||||
|
[Link (assoc props :active active) children]))
|
||||||
|
|
||||||
|
(defn current-page []
|
||||||
|
[:div
|
||||||
|
|
||||||
|
[:h4 "Link"]
|
||||||
|
[:ul
|
||||||
|
[:li [Link {:to ::frontpage} "Frontpage"]]
|
||||||
|
[:li [Link {:to "/about"} "About"]]
|
||||||
|
[:li [Link {:to ::item :path-params {:id 1}} "Item 1"]]
|
||||||
|
[:li [Link {:to "/item/2?foo=bar"} "Item 2"]]
|
||||||
|
[:li [Link {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
|
||||||
|
[:li [Link {:to "http://www.google.fi"} "Google"]]]
|
||||||
|
|
||||||
|
[:h4 "NavLink"]
|
||||||
|
[:ul
|
||||||
|
[:li [NavLink {:to ::frontpage} "Frontpage"]]
|
||||||
|
[:li [NavLink {:to "/about"} "About"]]
|
||||||
|
[:li [NavLink {:to ::item :path-params {:id 1}} "Item 1"]]
|
||||||
|
[:li [NavLink {:to "/item/2?foo=bar"} "Item 2"]]
|
||||||
|
[:li [NavLink {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
|
||||||
|
[:li [NavLink {:to "http://www.google.fi"} "Google"]]]
|
||||||
|
|
||||||
|
(if @current-match
|
||||||
|
(let [view (:view (:data @current-match))]
|
||||||
|
[view @current-match]))
|
||||||
|
|
||||||
|
[:pre (with-out-str (fedn/pprint @current-match))]])
|
||||||
|
|
||||||
|
(defn init! []
|
||||||
|
(rfe/start!
|
||||||
|
router
|
||||||
|
(fn [m] (reset! current-match m))
|
||||||
|
;; set to false to enable HistoryAPI
|
||||||
|
{:use-fragment true})
|
||||||
|
(r/render [current-page] (.getElementById js/document "app")))
|
||||||
|
|
||||||
|
(init!)
|
||||||
|
|
||||||
|
(comment
|
||||||
|
(rf/match-by-path router "/about?kissa=1&koira=true")
|
||||||
|
(rf/match-by-path router "/item/2?kissa=1&koira=true"))
|
||||||
13
examples/frontend-prompt/README.md
Normal file
13
examples/frontend-prompt/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# reitit-frontend example
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```clj
|
||||||
|
> lein figwheel
|
||||||
|
```
|
||||||
|
|
||||||
|
Go with browser to http://localhost:3449
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright © 2018 Metosin Oy
|
||||||
62
examples/frontend-prompt/project.clj
Normal file
62
examples/frontend-prompt/project.clj
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
(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.10.0"]
|
||||||
|
[ring-server "0.5.0"]
|
||||||
|
[reagent "0.8.1"]
|
||||||
|
[ring "1.7.1"]
|
||||||
|
[hiccup "1.0.5"]
|
||||||
|
[org.clojure/clojurescript "1.10.520"]
|
||||||
|
[metosin/reitit "0.3.1"]
|
||||||
|
[metosin/reitit-spec "0.3.1"]
|
||||||
|
[metosin/reitit-frontend "0.3.1"]
|
||||||
|
;; Just for pretty printting the match
|
||||||
|
[fipp "0.6.14"]]
|
||||||
|
|
||||||
|
:plugins [[lein-cljsbuild "1.1.7"]
|
||||||
|
[lein-figwheel "0.5.18"]
|
||||||
|
[cider/cider-nrepl "0.21.1"]]
|
||||||
|
|
||||||
|
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
|
||||||
|
|
||||||
|
:source-paths ["src"]
|
||||||
|
:resource-paths ["resources" "target/cljsbuild"]
|
||||||
|
|
||||||
|
:profiles
|
||||||
|
{:dev
|
||||||
|
{:dependencies
|
||||||
|
[[binaryage/devtools "0.9.10"]
|
||||||
|
[cider/piggieback "0.4.0"]
|
||||||
|
[figwheel-sidecar "0.5.18"]]}}
|
||||||
|
|
||||||
|
:cljsbuild
|
||||||
|
{:builds
|
||||||
|
[{:id "app"
|
||||||
|
:figwheel true
|
||||||
|
:source-paths ["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]
|
||||||
|
:aot-cache true}}
|
||||||
|
{: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
|
||||||
|
:aot-cache true}}]}
|
||||||
|
|
||||||
|
:figwheel {:http-server-root "public"
|
||||||
|
:server-port 3449
|
||||||
|
:nrepl-port 7002
|
||||||
|
;; Server index.html for all routes for HTML5 routing
|
||||||
|
:ring-handler backend.server/handler})
|
||||||
10
examples/frontend-prompt/resources/public/index.html
Normal file
10
examples/frontend-prompt/resources/public/index.html
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Reitit frontend example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="/js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
11
examples/frontend-prompt/src/backend/server.clj
Normal file
11
examples/frontend-prompt/src/backend/server.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
(ns backend.server
|
||||||
|
(:require [clojure.java.io :as io]
|
||||||
|
[ring.util.response :as resp]
|
||||||
|
[ring.middleware.content-type :as content-type]))
|
||||||
|
|
||||||
|
(def handler
|
||||||
|
(-> (fn [request]
|
||||||
|
(or (resp/resource-response (:uri request) {:root "public"})
|
||||||
|
(-> (resp/resource-response "index.html" {:root "public"})
|
||||||
|
(resp/content-type "text/html"))))
|
||||||
|
content-type/wrap-content-type))
|
||||||
70
examples/frontend-prompt/src/frontend/core.cljs
Normal file
70
examples/frontend-prompt/src/frontend/core.cljs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
(ns frontend.core
|
||||||
|
(:require [fipp.edn :as fedn]
|
||||||
|
[reagent.core :as r]
|
||||||
|
[reitit.coercion :as rc]
|
||||||
|
[reitit.coercion.spec :as rss]
|
||||||
|
[reitit.frontend :as rf]
|
||||||
|
[reitit.frontend.easy :as rfe]
|
||||||
|
[spec-tools.data-spec :as ds]))
|
||||||
|
|
||||||
|
;; Implementing conditional prompt on navigation with Reitit frontend.
|
||||||
|
|
||||||
|
(defn home-page []
|
||||||
|
[:div
|
||||||
|
[:h2 "Home"]
|
||||||
|
[:p "You will not be prompted to leave this page"]])
|
||||||
|
|
||||||
|
(defn prompt-page []
|
||||||
|
[:div
|
||||||
|
[:h2 "Prompt"]
|
||||||
|
[:p "You will be prompted to leave this page"]])
|
||||||
|
|
||||||
|
(def routes
|
||||||
|
[["/"
|
||||||
|
{:name ::home
|
||||||
|
:view home-page}]
|
||||||
|
|
||||||
|
["/prompt"
|
||||||
|
{:name ::prompt
|
||||||
|
:view prompt-page
|
||||||
|
;; Routes can contain arbitrary keys so we add custom :prompt
|
||||||
|
;; key here. See how it's handled in `on-navigate` function.
|
||||||
|
:prompt "Are you sure you want to leave?"
|
||||||
|
;; It would be possible to define a function here that resolves
|
||||||
|
;; wheter prompting is needed or not but we'll keep it simple.
|
||||||
|
}]])
|
||||||
|
|
||||||
|
(def router
|
||||||
|
(rf/router routes {:data {:coercion rss/coercion}}))
|
||||||
|
|
||||||
|
(defonce current-match (r/atom nil))
|
||||||
|
|
||||||
|
(defn current-page []
|
||||||
|
[:div
|
||||||
|
|
||||||
|
[:ul
|
||||||
|
[:li [:a {:href (rfe/href ::home)} "Home"]]
|
||||||
|
[:li [:a {:href (rfe/href ::prompt)} "Prompt page"]]]
|
||||||
|
|
||||||
|
(if @current-match
|
||||||
|
(let [view (-> @current-match :data :view)]
|
||||||
|
[view @current-match]))
|
||||||
|
[:pre (with-out-str (fedn/pprint @current-match))]])
|
||||||
|
|
||||||
|
(defn on-navigate [m]
|
||||||
|
(if-let [prompt (and (not= @current-match m)
|
||||||
|
(-> @current-match :data :prompt))]
|
||||||
|
(if (js/window.confirm prompt) ;; Returns true if OK is pressed.
|
||||||
|
(reset! current-match m)
|
||||||
|
(.back js/window.history)) ;; Restore browser location
|
||||||
|
(reset! current-match m)))
|
||||||
|
|
||||||
|
(defn init! []
|
||||||
|
(rfe/start!
|
||||||
|
router
|
||||||
|
on-navigate
|
||||||
|
;; set to false to enable HistoryAPI
|
||||||
|
{:use-fragment true})
|
||||||
|
(r/render [current-page] (.getElementById js/document "app")))
|
||||||
|
|
||||||
|
(init!)
|
||||||
26
examples/frontend-re-frame/README.md
Normal file
26
examples/frontend-re-frame/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frontend-re-frame
|
||||||
|
|
||||||
|
A [re-frame](https://github.com/Day8/re-frame) application designed to ... well, that part is up to you.
|
||||||
|
|
||||||
|
## Development Mode
|
||||||
|
|
||||||
|
### Run application:
|
||||||
|
|
||||||
|
```
|
||||||
|
lein clean
|
||||||
|
lein figwheel dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Figwheel will automatically push cljs changes to the browser.
|
||||||
|
|
||||||
|
Wait a bit, then browse to [http://localhost:3449](http://localhost:3449).
|
||||||
|
|
||||||
|
## Production Build
|
||||||
|
|
||||||
|
|
||||||
|
To compile clojurescript to javascript:
|
||||||
|
|
||||||
|
```
|
||||||
|
lein clean
|
||||||
|
lein cljsbuild once min
|
||||||
|
```
|
||||||
56
examples/frontend-re-frame/project.clj
Normal file
56
examples/frontend-re-frame/project.clj
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
(defproject frontend-re-frame "0.1.0-SNAPSHOT"
|
||||||
|
:dependencies [[org.clojure/clojure "1.10.0"]
|
||||||
|
[org.clojure/clojurescript "1.10.520"]
|
||||||
|
[metosin/reitit "0.3.1"]
|
||||||
|
[reagent "0.8.1"]
|
||||||
|
[re-frame "0.10.6"]]
|
||||||
|
|
||||||
|
:plugins [[lein-cljsbuild "1.1.7"]
|
||||||
|
[lein-figwheel "0.5.18"]
|
||||||
|
[cider/cider-nrepl "0.21.1"]]
|
||||||
|
|
||||||
|
:repl-options {:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
|
||||||
|
:min-lein-version "2.5.3"
|
||||||
|
:source-paths ["src/clj" "src/cljs"]
|
||||||
|
:clean-targets ^{:protect false} ["resources/public/js/compiled" "target"]
|
||||||
|
:figwheel
|
||||||
|
{:css-dirs ["resources/public/css"]
|
||||||
|
:server-port 3449
|
||||||
|
:nrepl-port 7002
|
||||||
|
:ring-handler backend.server/handler}
|
||||||
|
|
||||||
|
:profiles
|
||||||
|
{:dev
|
||||||
|
{:dependencies
|
||||||
|
[[binaryage/devtools "0.9.10"]
|
||||||
|
[cider/piggieback "0.4.0"]
|
||||||
|
[figwheel-sidecar "0.5.18"]]
|
||||||
|
|
||||||
|
:plugins [[lein-figwheel "0.5.18"]]}
|
||||||
|
:prod {}}
|
||||||
|
|
||||||
|
:cljsbuild
|
||||||
|
{:builds
|
||||||
|
[{:id "dev"
|
||||||
|
:source-paths ["src/cljs"]
|
||||||
|
:figwheel {:on-jsload "frontend-re-frame.core/mount-root"}
|
||||||
|
:compiler {:main frontend-re-frame.core
|
||||||
|
:output-to "resources/public/js/compiled/app.js"
|
||||||
|
:output-dir "resources/public/js/compiled/out"
|
||||||
|
:asset-path "js/compiled/out"
|
||||||
|
:source-map-timestamp true
|
||||||
|
:preloads [devtools.preload]
|
||||||
|
:external-config {:devtools/config {:features-to-install :all}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
{:id "min"
|
||||||
|
:source-paths ["src/cljs"]
|
||||||
|
:compiler {:main frontend-re-frame.core
|
||||||
|
:output-to "resources/public/js/compiled/app.js"
|
||||||
|
:optimizations :advanced
|
||||||
|
:closure-defines {goog.DEBUG false}
|
||||||
|
:pretty-print false}}
|
||||||
|
|
||||||
|
|
||||||
|
]}
|
||||||
|
)
|
||||||
13
examples/frontend-re-frame/resources/public/index.html
Normal file
13
examples/frontend-re-frame/resources/public/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="js/compiled/app.js"></script>
|
||||||
|
<script>frontend_re_frame.core.init();</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
11
examples/frontend-re-frame/src/clj/backend/server.clj
Normal file
11
examples/frontend-re-frame/src/clj/backend/server.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
(ns backend.server
|
||||||
|
(:require [clojure.java.io :as io]
|
||||||
|
[ring.util.response :as resp]
|
||||||
|
[ring.middleware.content-type :as content-type]))
|
||||||
|
|
||||||
|
(def handler
|
||||||
|
(-> (fn [request]
|
||||||
|
(or (resp/resource-response (:uri request) {:root "public"})
|
||||||
|
(-> (resp/resource-response "index.html" {:root "public"})
|
||||||
|
(resp/content-type "text/html"))))
|
||||||
|
content-type/wrap-content-type))
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
(ns frontend-re-frame.core)
|
||||||
155
examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs
Normal file
155
examples/frontend-re-frame/src/cljs/frontend_re_frame/core.cljs
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
(ns frontend-re-frame.core
|
||||||
|
(:require
|
||||||
|
[re-frame.core :as re-frame]
|
||||||
|
[reagent.core :as reagent]
|
||||||
|
[reitit.core :as r]
|
||||||
|
[reitit.coercion :as rc]
|
||||||
|
[reitit.coercion.spec :as rss]
|
||||||
|
[reitit.frontend :as rf]
|
||||||
|
[reitit.frontend.controllers :as rfc]
|
||||||
|
[reitit.frontend.easy :as rfe]))
|
||||||
|
|
||||||
|
;;; Events ;;;
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
::initialize-db
|
||||||
|
(fn [_ _]
|
||||||
|
{:current-route nil}))
|
||||||
|
|
||||||
|
(re-frame/reg-event-fx
|
||||||
|
::navigate
|
||||||
|
(fn [db [_ route]]
|
||||||
|
;; See `navigate` effect in routes.cljs
|
||||||
|
{::navigate! route}))
|
||||||
|
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
::navigated
|
||||||
|
(fn [db [_ new-match]]
|
||||||
|
(let [old-match (:current-route db)
|
||||||
|
controllers (rfc/apply-controllers (:controllers old-match) new-match)]
|
||||||
|
(assoc db :current-route (assoc new-match :controllers controllers)))))
|
||||||
|
|
||||||
|
;;; Subscriptions ;;;
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
::current-route
|
||||||
|
(fn [db]
|
||||||
|
(:current-route db)))
|
||||||
|
|
||||||
|
;;; Views ;;;
|
||||||
|
|
||||||
|
(defn home-page []
|
||||||
|
[:div
|
||||||
|
[:h1 "This is home page"]
|
||||||
|
[:button
|
||||||
|
;; Dispatch navigate event that triggers a (side)effect.
|
||||||
|
{:on-click #(re-frame/dispatch [::navigate ::sub-page2])}
|
||||||
|
"Go to sub-page 2"]])
|
||||||
|
|
||||||
|
(defn sub-page1 []
|
||||||
|
[:div
|
||||||
|
[:h1 "This is sub-page 1"]])
|
||||||
|
|
||||||
|
(defn sub-page2 []
|
||||||
|
[:div
|
||||||
|
[:h1 "This is sub-page 2"]])
|
||||||
|
|
||||||
|
;;; Effects ;;;
|
||||||
|
|
||||||
|
;; Triggering navigation from events.
|
||||||
|
(re-frame/reg-fx
|
||||||
|
::navigate!
|
||||||
|
(fn [k params query]
|
||||||
|
(rfe/push-state k params query)))
|
||||||
|
|
||||||
|
;;; Routes ;;;
|
||||||
|
|
||||||
|
(defn href
|
||||||
|
"Return relative url for given route. Url can be used in HTML links."
|
||||||
|
([k]
|
||||||
|
(href k nil nil))
|
||||||
|
([k params]
|
||||||
|
(href k params nil))
|
||||||
|
([k params query]
|
||||||
|
(rfe/href k params query)))
|
||||||
|
|
||||||
|
(def routes
|
||||||
|
["/"
|
||||||
|
[""
|
||||||
|
{:name ::home
|
||||||
|
:view home-page
|
||||||
|
:link-text "Home"
|
||||||
|
:controllers
|
||||||
|
[{;; Do whatever initialization needed for home page
|
||||||
|
;; I.e (re-frame/dispatch [::events/load-something-with-ajax])
|
||||||
|
:start (fn [& params](js/console.log "Entering home page"))
|
||||||
|
;; Teardown can be done here.
|
||||||
|
:stop (fn [& params] (js/console.log "Leaving home page"))}]}]
|
||||||
|
["sub-page1"
|
||||||
|
{:name ::sub-page1
|
||||||
|
:view sub-page1
|
||||||
|
:link-text "Sub page 1"
|
||||||
|
:controllers
|
||||||
|
[{:start (fn [& params] (js/console.log "Entering sub-page 1"))
|
||||||
|
:stop (fn [& params] (js/console.log "Leaving sub-page 1"))}]}]
|
||||||
|
["sub-page2"
|
||||||
|
{:name ::sub-page2
|
||||||
|
:view sub-page2
|
||||||
|
:link-text "Sub-page 2"
|
||||||
|
:controllers
|
||||||
|
[{:start (fn [& params] (js/console.log "Entering sub-page 2"))
|
||||||
|
:stop (fn [& params] (js/console.log "Leaving sub-page 2"))}]}]])
|
||||||
|
|
||||||
|
(defn on-navigate [new-match]
|
||||||
|
(when new-match
|
||||||
|
(re-frame/dispatch [::navigated new-match])))
|
||||||
|
|
||||||
|
(def router
|
||||||
|
(rf/router
|
||||||
|
routes
|
||||||
|
{:data {:coercion rss/coercion}}))
|
||||||
|
|
||||||
|
(defn init-routes! []
|
||||||
|
(js/console.log "initializing routes")
|
||||||
|
(rfe/start!
|
||||||
|
router
|
||||||
|
on-navigate
|
||||||
|
{:use-fragment true}))
|
||||||
|
|
||||||
|
(defn nav [{:keys [router current-route]}]
|
||||||
|
[:ul
|
||||||
|
(for [route-name (r/route-names router)
|
||||||
|
:let [route (r/match-by-name router route-name)
|
||||||
|
text (-> route :data :link-text)]]
|
||||||
|
[:li {:key route-name}
|
||||||
|
(when (= route-name (-> current-route :data :name))
|
||||||
|
"> ")
|
||||||
|
;; Create a normal links that user can click
|
||||||
|
[:a {:href (href route-name)} text]])])
|
||||||
|
|
||||||
|
(defn router-component [{:keys [router]}]
|
||||||
|
(let [current-route @(re-frame/subscribe [::current-route])]
|
||||||
|
[:div
|
||||||
|
[nav {:router router :current-route current-route}]
|
||||||
|
(when current-route
|
||||||
|
[(-> current-route :data :view)])]))
|
||||||
|
|
||||||
|
;;; Setup ;;;
|
||||||
|
|
||||||
|
(def debug? ^boolean goog.DEBUG)
|
||||||
|
|
||||||
|
(defn dev-setup []
|
||||||
|
(when debug?
|
||||||
|
(enable-console-print!)
|
||||||
|
(println "dev mode")))
|
||||||
|
|
||||||
|
(defn mount-root []
|
||||||
|
(re-frame/clear-subscription-cache!)
|
||||||
|
(init-routes!) ;; Reset routes on figwheel reload
|
||||||
|
(reagent/render [router-component {:router router}]
|
||||||
|
(.getElementById js/document "app")))
|
||||||
|
|
||||||
|
(defn ^:export init []
|
||||||
|
(re-frame/dispatch-sync [::initialize-db])
|
||||||
|
(dev-setup)
|
||||||
|
(mount-root))
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
([match]
|
([match]
|
||||||
(match->path match nil))
|
(match->path match nil))
|
||||||
([match query-params]
|
([match query-params]
|
||||||
(some-> match :path (cond-> query-params (str "?" (impl/query-string query-params))))))
|
(some-> match :path (cond-> (seq query-params) (str "?" (impl/query-string query-params))))))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
;; Different routers
|
;; Different routers
|
||||||
|
|
|
||||||
|
|
@ -354,6 +354,10 @@
|
||||||
(-> router
|
(-> router
|
||||||
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
(r/match->path))))
|
(r/match->path))))
|
||||||
|
(is (= "/olipa/kerran"
|
||||||
|
(-> router
|
||||||
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
(r/match->path {}))))
|
||||||
(is (= "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen"
|
(is (= "/olipa/kerran?iso=p%C3%B6ril%C3%A4inen"
|
||||||
(-> router
|
(-> router
|
||||||
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
(r/match-by-name! ::route {:a "olipa", :b "kerran"})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue