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 ofgoog.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!)
Hmm, a couple more questions on this.
This causes route changes when you click on any element, including the document (which will navigate to an empty href). You can get around that with something like:
(if (= (.-nodeName (.-target e)) "A") ...)
It doesn't handle
<a>
tags that have nested components. E.g. an<a><img src="..."></a>
any thoughts on handling that? Perhaps it could inspect up the dom tree looking for parent<a>
tags and use them?