iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0
Modern Web

後端Developer實戰ClojureScript: Reagent與前端框架 Reframe系列 第 26

[Day26] 從ClojureScript 到 Reagent (3之3) 路由 route簡介. 設計文章列表

  • 分享至 

  • xImage
  •  

[Day26] 從ClojureScript 到 Reagent (3之3) 路由 route簡介. 設計文章列表

早安!昨天我們完成了增加component,並且做了一點點的優化

  • Home 首頁 用component調整一下code
  • About 關於 用hiccup語法增加IT邦鐵人賽系列超連結
  • Page mounting component 每頁都要用到的code和component可以寫在current-page function

GitHub程式碼在此

今天的自我練習題:

每次參加鐵人賽,我們都會在短短一個月內產出非常多篇技術文章,
因此我今天想要在Reagent app
增加可以收錄這些文章的link (如下圖紅框處)
為自己做個紀念~

想像中,我點進去 My 2022 Articles時的網頁結構會類似是這樣的

那我們觀察到網址 localhost/articles是怎麼被設計出來的呢?

route

Route功用是可以幫助我們指定路徑

以Ruby on RailsMVC (Model, View, and Controller)架構來說,使用者在瀏覽器輸入了某個路徑,Route會幫我們指定到 controller中對應的action

那ClojureScript的設計是如何做到的呢? 我們來研究一下reitit 這個data-driven router for Clojure(Script)的套件。

可以從doc先學簡單的sytax做練習:)

Reitit route 設計

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對應到每篇文章的頁面

將routes對應到page components

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

articles-page component

目前IT鐵人賽文章已經進行到了第26天,所以我們可以在文章列表componentarticles-page利用map方式幫我們產生出1~26篇文章連結

  • code
(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))]]))
  • UI

article-page component

每篇文章自己的component article-page

  • code
(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"]]])))
  • UI

其中 defn articles-pagedefn 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分隔線 ^_^


上一篇
[Day25] 從ClojureScript 到 Reagent (3之2) 建立元件 component
下一篇
[Day27] 從ClojureScript 到 Reagent (4) Atom / swap! / deref
系列文
後端Developer實戰ClojureScript: Reagent與前端框架 Reframe30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言