Skip to content

A router for clojurescript based on secretary and goog.html5history.

Notifications You must be signed in to change notification settings

mkhoeini/router

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 

Repository files navigation

router

A Clojurescript library designed to help routing in the browser.

Usage

Include the dependency in your project.clj.

latest version

Then require the router.core in your cljs files.

(ns your-namespace
  (:require [router.core :as router :include-macros true]))

Define your routes with the match macro. The syntax is of the secretary. However, it is simplified in that it will accept only a value, and doesn’t pass parameters to the body.

(router/match "/" :root-path)
(router/match "/user/:id" :user-path)
(router/match "/admin/*path" :admin-path)
(router/match #"/search/([a-zA-Z]*)" :search-path)

Configure the history manager.

(router/config!)

Or, pass some options. The defaults are:

(router/config! {:use-fragment false    ;; Use '/#/some/path' style pathes
                 :path-prefix ""        ;; The path prefix. For example "!"
                 :enabled true})        ;; Enable the manager

Later on, you can enable or disable the manager with

(router/set-enabled!)
(router/set-disabled!)

And, finally you can register your callback to be called on routing.

(defn callback
  "Callback should take two params:
  value: The value you associated with the current path.
  params: The returned parameters of current route from secretary."
  [value params]
  (pr "We are at" value "with params" params))

(router/set-callback! your-callback)

The current route is (router/current-route).

You can navigate to a new route with (router/navigate! "/new/route").

Also, you can prevent the default behaviour of links and buttons from refreshing the window with (router/prevent-default-refresh!).

React

In the router.react namespace resides a default callback which you can use to mount your pages to specific DOM node.

(ns some-ns
  (:require [router.core :refer-macros (match)]
            [router.react :as rr]))

(match "/some-route" some-react-element)
(match "/other-route" (fn [p] construct-and-return-an-react-element))

(rr/initiate! (.-body js/document))

router.react expects that the values you associated with the routes are either React elements or functions of one parameter which return react elements. The parameter is params of the route.

On page navigation the supplied React element will be rendered on the DOM node.

You can pass a custom callback to transform the event before it acts upon it, or do any other thing with it.

(defn optional-callback
  "It must accept two params:
    value: The assosiated value with the route
    params: The route's params
  And returns a vector of two: The transformed value and params."
  [value params]
  (pr "recieved" value params)
  [value params])

Reagent

The router.reagent namespace provides a different match macro to facilitate using Reagent components.

(ns some-ns
  (:require [router.reagent :refer-macros (match)]))

(match "/some-path" {:keys [param1] :as route-params} [reagent-component param1 param2 ...])

This macro delegates to the original router.core/match macro and assosiates with the route a function with the shape that callback of router.react requires.

What Is a Router

    Dispatcher:   System  ---->  Route Objects*
    Commiter:     System  <----  Route Object

    Routing Table:
    [
      name:    route object  <-- assosiation -->  route value ,
      name:    route object  <-- assosiation -->  route value ,
      name:    route object  <-- assosiation -->  route value ,
      ...
    ]

Routing is a separate concern from application logic. By complecting routing with logic only a little can be obtained, and a lot will be lost as a result. It is better that we create a composable and flexible routing system which can be bent to different use cases, but by default it does the right thing: no logic in routing.

A router in its simplest form is a set of three things: Dispatcher, Commiter, Routing Table. Dispatcher and Commiter are system specific. For example, browser tailored, ring tailored, memcache tailored, etc. But Routing Table is application specific.

Usually the Dispatcher and Commiter are provided for you, and you only create a routing table. However, Dispatcher and Commiter are the points of composition. And they can provide the means to the infinite flexibility.

The dispatcher-commiter duo establish a one to one correspondence between the system state and Routing objects. The Dispatcher component listens to the system state and emits new Route objects on the system transition. And the commiter receives Route objects and transitions the system state to match the route object.

Now, the routing table is almost the same thing, if you’ve made dispatcher-commiter application specific and partial.

Each association in the Routing Table is a uniqely named bidirectional partial function between a system domain representation of state and an application domain representation of state. What this means is that if you pass a route object to an assosiation, it might return you an analogy of it in terms of the application. For example, for something like "api.example.com/v2/users/123", an association named users might return an object JohnDoe. And the other direction must work as well. Which means, association users must uniqely map JohnDoe to the route object "api.example.com/v2/users/123".

Further Ideas

About

A router for clojurescript based on secretary and goog.html5history.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published