早安!昨天我們完成了增加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分隔線 ^_^)
