goog.History, but something felt wrong still.
Things have changed and today there’s a lot buzz about React-based libraries like Reagent and Om. I suspect that React on the front with a bunch of “native” ClojureScript libraries may be a better way to go.
Let’s say our app has two screens which fill the entire page. There are no various “fragments” to compose the page from yet. We want to see one page when we navigate to
/#/add and another at
/#/browse. The “browse” page will be a little bit more advanced and support path parameters. For example, for
/#/browse/Stuff we want to parse the “Stuff” and display a header with this word.
The main HTML could look like:
<!DOCTYPE html> <html> <body> <div class="container-fluid"> <div id="view">Loading...</div> </div> <script src="js/main.js"></script> </body> </html>
Then we have two templates.
<h1>Add things</h1> <form> <!-- boring, omitted --> </form>
<h1></h1> <div> <!-- boring, omitted --> </div>
Now, all we want to do is to fill the
#view element on the main page with one of the templates when location changes. The complete code for this is below.
(ns my.main (:require [secretary.core :as secretary :include-macros true :refer [defroute]] [goog.events :as events] [enfocus.core :as ef]) (:require-macros [enfocus.macros :as em]) (:import goog.History goog.History.EventType)) (em/deftemplate view-add "templates/add.html" ) (em/deftemplate view-browse "templates/browse.html" [category] ["h1"] (ef/content category)) (defroute "/"  (.setToken (History.) "/add")) (defroute "/add"  (em/wait-for-load (ef/at ["#view"] (ef/content (view-add))))) (defroute "/browse/:category" [category] (em/wait-for-load (ef/at ["#view"] (ef/content (view-browse category))))) (doto (History.) (goog.events/listen EventType/NAVIGATE #(secretary/dispatch! (.-token %))) (.setEnabled true))
What’s going on?
- We define two Enfocus templates.
view-addis trivial and simply returns the entire template.
view-browseis a bit more interesting: Given category name, alter the template by replacing content of
h1tag with the category name.
- Then we define Secretary routes to actually use those templates. All they do now is replace content of the
#viewelement with the template. In case of the “browse” route, it passes the category name parsed from path to the template.
- There is a default route that redirects from
/add. It doesn’t lead to
example.com/add, but only sets the fragment:
- Note that all templating is wrapped in
em/wait-for-load. This bit seems to be necessary for Enfocus if you load templates with AJAX calls, and I haven’t found a way yet to do it once per app. It was the hardest part to get to work and I’m still confused about it.
- Finally, we plug in Secretary to
goog.History. I’m not sure why it’s not in the box, but it’s straightforward enough.