mirror of
https://github.com/metosin/reitit.git
synced 2025-12-16 16:01:11 +00:00
Add examples inspired by react-router
This commit is contained in:
parent
9241de9a43
commit
215884abe3
10 changed files with 405 additions and 0 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))
|
||||
143
examples/frontend-links/src/frontend/core.cljs
Normal file
143
examples/frontend-links/src/frontend/core.cljs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
(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]
|
||||
(redirect! props)
|
||||
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 (not-empty (or path-params (:path-params match)))
|
||||
query (not-empty (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!)
|
||||
Loading…
Reference in a new issue