Skip to content

Commit

Permalink
Merge pull request #22 from arohner/master
Browse files Browse the repository at this point in the history
Make accountant secretary-agnostic
  • Loading branch information
venantius committed Feb 20, 2016
2 parents 8726f0a + 28c688d commit 4a8149a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 17 deletions.
39 changes: 36 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Accountant

Accountant is a ClojureScript library to make navigation in single-page
applications simple. It expects you to use [Secretary](https://github.com/gf3/secretary) to define your routes.
applications simple.

By default, clicking a link in a ClojureScript application that isn't a simple
URL fragment will trigger a full page reload. This defeats the purpose of using
Expand Down Expand Up @@ -33,10 +33,41 @@ All you have to do to get Accountant working is the following:
(ns your-app-ns
(:require [accountant.core :as accountant]))

(accountant/configure-navigation!)
(accountant/configure-navigation! {:nav-handler (fn [path] ...) :path-exists? (fn [path] ...)})
```

...and you're good to go!
nav-handler is a fn of one argument, the path we're about to navigate to. You'll want to make whatever side-effect you need to render the page here. If you're using secretary, it'd look something like:

```clojure
(fn [path]
(secretary/dispatch! path))
```

If you're using bidi + just rendering via react, that might look like:

```clojure
(fn [path]
(om/update! app [:path] path))
```

`path-exists?` is a fn of one argument, a path, that takes the path
we're about to navigate to. This should return truthy if the path is
handled by your SPA, because accountant will preventDefault the event, to
prevent the browser from doing a full page request.

Using secretary, path-exists? would look like:

```clojure
(fn [path]
(secretary/locate-route path))
```

Using bidi, path-exists? would look like:

```clojure
(fn [path]
(boolean (bidi/match-route app-routes path)))
```

You can also use Accountant to set the current path in the browser, e.g.

Expand All @@ -50,6 +81,8 @@ If you want to dispatch the current path, just add the following:
(dispatch-current!)
```

Note that both `navigate!` and `dispatch-current!` can only be used after calling `configure-navigation!`

## License

Copyright © 2015 W. David Jarvis
Expand Down
1 change: 0 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.7.0"]
[org.clojure/clojurescript "1.7.48"]
[secretary "1.2.3"]
[org.clojure/core.async "0.1.346.0-17112a-alpha"]])
39 changes: 26 additions & 13 deletions src/accountant/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [cljs.core.async :refer [put! <! chan]]
[clojure.string :as str]
[secretary.core :as secretary]
[goog.events :as events]
[goog.history.EventType :as EventType])
(:import goog.history.Event
Expand All @@ -19,12 +18,12 @@
out))

(defn- dispatch-on-navigate
[history]
[history nav-handler]
(let [navigation (listen history EventType/NAVIGATE)]
(go
(while true
(let [token (.-token (<! navigation))]
(secretary/dispatch! token))))))
(nav-handler token))))))

(defn- find-href
"Given a DOM element that may or may not be a link, traverse up the DOM tree
Expand Down Expand Up @@ -61,9 +60,8 @@
(str "#" fragment))))

(defn- prevent-reload-on-known-path
"Create a click handler that blocks page reloads for known routes in
Secretary."
[history]
"Create a click handler that blocks page reloads for known routes"
[history path-exists?]
(events/listen
js/document
"click"
Expand All @@ -84,18 +82,29 @@
title (.-title target)
host (.getDomain uri)
current-host js/window.location.hostname]
(when (and (not any-key) (= button 0) (secretary/locate-route path) (= host current-host))
(when (and (not any-key) (= button 0) (path-exists? path) (= host current-host))
(set-token! history relative-href title)
(.preventDefault e))))))

(defonce nav-handler nil)
(defonce path-exists? nil)

(defn configure-navigation!
"Create and configure HTML5 history navigation."
[]
"Create and configure HTML5 history navigation.
nav-handler: a fn of one argument, a path. Called when we've decided
to navigate to another page. You'll want to make your app draw the
new page here.
path-exists?: a fn of one argument, a path. Return truthy if this path is handled by the SPA"
[{:keys [nav-handler path-exists?]}]
(.setUseFragment history false)
(.setPathPrefix history "")
(.setEnabled history true)
(dispatch-on-navigate history)
(prevent-reload-on-known-path history))
(set! accountant.core/nav-handler nav-handler)
(set! accountant.core/path-exists? path-exists?)
(dispatch-on-navigate history nav-handler)
(prevent-reload-on-known-path history path-exists?))

(defn map->params [query]
(let [params (map #(name %) (keys query))
Expand All @@ -107,6 +116,7 @@
"add a browser history entry. updates window/location"
([route] (navigate! route {}))
([route query]
(if nav-handler
(let [token (.getToken history)
old-route (first (str/split token "?"))
query-string (map->params (reduce-kv (fn [valid k v]
Expand All @@ -118,11 +128,14 @@
(str route "?" query-string))]
(if (= old-route route)
(. history (replaceToken with-params))
(. history (setToken with-params))))))
(. history (setToken with-params))))
(js/console.error "can't navigate! until configure-navigation! called"))))

(defn dispatch-current! []
"Dispatch current URI path."
(let [path (-> js/window .-location .-pathname)
query (-> js/window .-location .-search)
hash (-> js/window .-location .-hash)]
(secretary/dispatch! (str path query hash))))
(if nav-handler
(nav-handler (str path query hash))
(js/console.error "can't dispatch-current until configure-navigation! called"))))

0 comments on commit 4a8149a

Please sign in to comment.