Skip to content

Instantly share code, notes, and snippets.

@city41
Last active January 19, 2021 12:51
Show Gist options
  • Save city41/aab464ae6c112acecfe1 to your computer and use it in GitHub Desktop.
Save city41/aab464ae6c112acecfe1 to your computer and use it in GitHub Desktop.
ClojureScript secretary client side navigation without hashes

This is the example that comes with the reagent template converted to use HTML5 based history. This means there are no # in the urls.

I just got this working, so there might be better approaches

The changes are

  • use goog.history.Html5history instead of goog.History
  • listen to clicks on the page, extract the path from them, and push them onto the history
  • listen to history changes, and have secretary do its thing in response
(ns routing.core
    (:require [reagent.core :as reagent :refer [atom]]
              [reagent.session :as session]
              [secretary.core :as secretary :include-macros true]
              [goog.events :as events]
              [goog.history.EventType :as EventType])
    (:import goog.history.Html5History
             goog.Uri))

;; -------------------------
;; Views

(defn home-page []
  [:div [:h2 "Welcome to routing"]
   [:div [:a {:href "/about"} "go to about page"]]])

(defn about-page []
  [:div [:h2 "About routing"]
   [:div [:a {:href "/"} "go to the home page"]]])

(defn current-page []
  [:div [(session/get :current-page)]])

;; -------------------------
;; Routes

(secretary/defroute "/" []
  (session/put! :current-page home-page))

(secretary/defroute "/about" []
  (session/put! :current-page about-page))

;; -------------------------
;; Initialize app
(defn init! []
  (reagent/render-component [current-page] (.getElementById js/document "app")))

;; -------------------------
;; History
(defn hook-browser-navigation! []
  (let [history (doto (Html5History.)
                  (events/listen
                    EventType/NAVIGATE
                    (fn [event]
                      (secretary/dispatch! (.-token event))))
                  (.setUseFragment false)
                  (.setPathPrefix "")
                  (.setEnabled true))]

    (events/listen js/document "click"
                   (fn [e]
                     (. e preventDefault)
                     (let [path (.getPath (.parse Uri (.-href (.-target e))))
                           title (.-title (.-target e))]
                       (when path
                         (. history (setToken path title))))))))

;; need to run this after routes have been defined
(hook-browser-navigation!)
@aaronblenkush
Copy link

Thanks for this. I ended up using the following form for the body of the let in hook-browser-navigation! which will allow normal hyperlinks to work if they didn't match a secretary route.

(events/listen js/document "click"
               (fn [e]
                 (let [path (.getPath (.parse Uri (.-href (.-target e))))
                       title (.-title (.-target e))]
                   (when (secretary/locate-route path)
                     (. e preventDefault)
                     (. history (setToken path title))))))

@jdkealy
Copy link

jdkealy commented Aug 11, 2015

Hi,

Thanks for this! it works very well! Any idea why the app gets re-rendered upon clicking something / anything ?

@venantius
Copy link

For those of you looking for a highly consumable version of this, I'd recommend checking out a small lib I made for exactly this purpose - https://github.com/venantius/accountant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment