Skip to content

Instantly share code, notes, and snippets.

@scttnlsn
Created July 1, 2015 17:58
Show Gist options
  • Save scttnlsn/4ad7f7add0dfc773bdbf to your computer and use it in GitHub Desktop.
Save scttnlsn/4ad7f7add0dfc773bdbf to your computer and use it in GitHub Desktop.
Reagent/Secretary nested routing
(ns nested-routing.core
(:require [goog.events :as events]
[goog.history.EventType :as EventType]
[reagent.core :as reagent]
[reagent.ratom :refer-macros [reaction]]
[re-frame.core :refer [dispatch dispatch-sync register-handler register-sub subscribe]]
[secretary.core :as secretary :refer-macros [defroute]])
(:import goog.History))
(declare route-components
home-path
posts-path
post-path)
(defn outlet [route]
(let [current-route (subscribe [:current-route])
depth #(or (.. % -context -outletdepth) 0)]
(reagent/create-class
{:display-name "Outlet"
:context-types
#js {:outletdepth js/React.PropTypes.number}
:child-context-types
#js {:outletdepth js/React.PropTypes.number.isRequired}
:get-child-context
(fn [this]
#js {:outletdepth (inc (depth this))})
:reagent-render
(fn []
(let [this (reagent/current-component)
index (depth this)
components (get route-components (:name @current-route))]
(if (> (count components) index)
[(nth components index)]
nil)))})))
;; queries
(defn find-post [db id]
(first (filter (comp #{id} :id) (:posts db))))
;; subscriptions
(register-sub :current-route
(fn [db]
(reaction (:route @db))))
(register-sub :posts
(fn [db]
(reaction (:posts @db))))
(register-sub :post
(fn [db [_ id]]
(reaction (find-post @db id))))
(register-sub :current-post
(fn [db]
(let [current-route (subscribe [:current-route])]
(reaction (find-post @db (-> @current-route :params :id))))))
;; handlers
(register-handler :initialize
(fn [db [_ data]]
(merge db data)))
(register-handler :navigate
(fn [db [_ name params]]
(assoc db :route {:name name :params params})))
;; components
(defn main []
[:div
[:h1
[:a {:href (home-path)} "Nested Routing Example"]]
[outlet]])
(defn home []
[:a {:href (posts-path)} "View posts"])
(defn post-item [post]
(let [current-post (subscribe [:current-post])]
(fn [post]
(let [active (= post @current-post)]
[:li
[:a {:class (if active "active" "")
:href (post-path post)}
(:title post)]]))))
(defn posts []
(let [posts (subscribe [:posts])]
(fn []
[:div
[:a {:href (posts-path)} "Posts:"]
(for [post @posts]
^{:key (:id post)}
[post-item post])
[outlet]])))
(defn post-detail []
(let [post (subscribe [:current-post])]
(fn []
[:p (:details @post)])))
;; routing
(def route-components
{:home [main home]
:post-list [main posts]
:post-detail [main posts post-detail]})
(defroute home-path "/" []
(dispatch [:navigate :home {}]))
(defroute posts-path "/posts" []
(dispatch [:navigate :post-list {}]))
(defroute post-path "/posts/:id" [id]
(dispatch [:navigate :post-detail {:id (js/parseInt id 10)}]))
;; main
(enable-console-print!)
(secretary/set-config! :prefix "#")
(def init-data {:route {:name :home
:params {}}
:posts [{:id 1
:title "Lorem ipsum"
:details "Lorem ipsum dolor sit amet, consectetur adipiscing elit."}
{:id 2
:title "Morbi porta"
:details "Morbi porta tortor mauris, sit amet tincidunt libero malesuada nec."}]})
(dispatch-sync [:initialize init-data])
(let [h (History.)]
(goog.events/listen h EventType/NAVIGATE #(secretary/dispatch! (.-token %)))
(doto h (.setEnabled true)))
(reagent/render-component [outlet] (. js/document (getElementById "app")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment