早安!昨天我們完成了增加component,並且做了一點點的優化
Home 首頁
用component調整一下codeAbout 關於
用hiccup語法增加IT邦鐵人賽系列超連結Page mounting component
每頁都要用到的code和component可以寫在current-page function今天的自我練習題:
每次參加鐵人賽,我們都會在短短一個月內產出非常多篇技術文章,
因此我今天想要在Reagent app
增加可以收錄這些文章的link (如下圖紅框處)
為自己做個紀念~
想像中,我點進去 My 2022 Articles
時的網頁結構會類似是這樣的
那我們觀察到網址 localhost/articles
是怎麼被設計出來的呢?
Route功用是可以幫助我們指定路徑
以Ruby on RailsMVC (Model, View, and Controller)架構
來說,使用者在瀏覽器輸入了某個路徑,Route會幫我們指定到 controller中對應的action
那ClojureScript的設計是如何做到的呢? 我們來研究一下reitit 這個data-driven router for Clojure(Script)的套件。
可以從doc先學簡單的sytax做練習:)
Simple route可以寫在一個vector:
["/index"]
多個routes,vector裡再包vector:
[["/index"]
["/about"]]
有route arguments的設計:
[["/about" ::about]
["/articles" {:name ::articles}]]
有路徑參數parameters (eg.帶id, 版本):
[["/users/:user-id"]
;; 或者也可以是
["/users/{user-id}"]
;; 不同的api / 檔案版本
["/api/:version/fund"]
["/files/file-{version}.pdf"]]
巢狀routes:
["/api"
["/admin" {:middleware [::admin]}
["" ::admin]
["/db" ::db]]
["/fund" ::fund]]
巢狀routes被flattened過後的樣子
[["/api/admin" {:middleware [::admin], :name ::admin}]
["/api/admin/db" {:middleware [::admin], :name ::db}]
["/api/fund" {:name ::fund}]]
在reitit裡最重要的概念是routes data,
我們可以利用def binding定義產生路徑(組data)的方式
Routes can have any map-like data attached to them, to be interpreted by the client application, Router or routing components like Middleware or Interceptors.
(defn routes [actions]
["/api" {:interceptors [::api ::db]}
(for [[type interceptor] actions
:let [path (str "/" (name interceptor))
method (case type
:query :get
:command :post)]]
[path {method {:interceptors [interceptor]}}])])
Reagent App
route arguments/articles
觀察Reagent template的 def router
binding
原本的首頁
和關於
頁面的路徑長這樣
(def router
(reitit/router
[["/" :index]
["/about" :about]]))
以上會為我們產生
如果再多加一個/articles
要如何設計呢?
很簡單,多加一個array["/articles" :articles]
就成了
組合起來變成這樣:
(def router
(reitit/router
[["/" :index]
["/articles" :articles]
["/about" :about]]))
/articles/id
我們希望點進去articles列表之後
可以再點入每一天的文章觀看內容
而每一篇文章的網址我想要用以下的方式先簡單表達就好
http://localhost:3000/articles/1
http://localhost:3000/articles/2
http://localhost:3000/articles/3
所以要把 ["/articles" :articles]
變成巢狀routes
(def router
(reitit/router
[["/" :index]
["/articles"
["" :articles]
["/:article-id" :article]]
["/about" :about]]))
["/:article-id" :article]
這樣的設計
讓id對應到每篇文章的頁面
route的argument已經設計完成,
;; Translate routes -> page components
(defn page-for [route]
(case route
:index #'home-page
:about #'about-page
:articles #'articles-page
:article #'article-page))
上一篇文章已經學會設計每頁的component,我們可以讓:keyword
對應每篇的page component
:articles
文章列表component articles-page
:article
每篇文章自己的component article-page
目前IT鐵人賽文章已經進行到了第26天,所以我們可以在文章列表componentarticles-page
利用map方式幫我們產生出1~26篇文章連結
(defn articles-page []
(fn []
[:span.main
[:h1 "Articles of 2022 Ironproject"]
[:ul (map (fn [article-id]
[:li {:name (str "article" article-id) :key (str "article-" article-id)}
[:a {:href (path-for :article {:article-id article-id})} "Day" article-id]])
(range 1 27))]]))
每篇文章自己的component article-page
(defn article-page []
(fn []
(let [routing-data (session/get :route)
article (get-in routing-data [:route-params :article-id])]
[:span.main
[:h1 (str "Article " article " of ironproject")]
[:p [:a {:href (path-for :articles)} "Back to the list of articles"]]])))
其中 defn articles-page
和defn article-page
裡面都有 path-for
function,對應到有參數帶入(articles/1)和沒有參數(articles)的情況
(defn path-for [route & [params]]
(if params
(:path (reitit/match-by-name router route params))
(:path (reitit/match-by-name router route))))
完成~
可以開始把我們今天的文章寫進Reagent App的頁面囉!
(還有可愛的kiss component
當作footer分隔線 ^_^
)