Om Next routing with om.next/set-query!

An implementation of client side routing using the client-side router library secretary and om.next/set-query!. Note: this solution may become outdated when Routing-Hooks is made available. Further reading on Om Next routing, see: Routing-Support.

Also take a look first at: Routing in Om Next — a Catalog of Approaches, which describes Om Next routing variants without third party client-side router libraries. (In fact this catalog of approaches obsoletes my article so you might as well stop reading and go the the above link directly.)

GitHub

The code for this example is on GitHub.

Implementation

(excuse my Clojure –rookie)

  1(ns yourproject.app
  2  (:require [om.next :as om :refer-macros [defui]]
  3            [om.dom :as dom]
  4            [secretary.core :as secretary]
  5
  6            [clojure.string :as s]
  7            [yourproject.util :as util]
  8
  9            [yourproject.reconciler :refer [reconciler]]
 10            [yourproject.parsers.app :as app-parser]
 11
 12            [yourproject.navbar :refer [Navbar navbar]]
 13            [yourproject.pages.home :refer [HomePage home-page]]
 14            [yourproject.pages.browse :refer [BrowsePage browse-page]]
 15            [yourproject.pages.about :refer [AboutPage about-page]]))
 16
 17;;-----------
 18;; Constants.
 19(def pages
 20  "Information on pages. Update this when new pages are added. Also
 21  update the routes in the routing namespace. Beware: (a) keywords
 22  *must* start with the :page+ prefix. (b) the URL /home is an alias
 23  for / (under the hood) so don't define a /home entry."
 24
 25  {"/" [:page+home
 26        (om/get-query HomePage)
 27        home-page]
 28   "/browse" [:page+browse
 29              (om/get-query BrowsePage)
 30              browse-page]
 31   "/about" [:page+about
 32             (om/get-query AboutPage)
 33             about-page]})
 34
 35;;---------
 36;; Queries.
 37(def app-query
 38  "Application level query."
 39  {:app [:logged-in?]})
 40
 41(def navbar-query
 42  "Navbar query"
 43  {:navbar (om/get-query Navbar)})
 44
 45(defn page-query
 46  "Gets query by page keyword and page subquery."
 47  [kw sq]
 48  {kw sq})
 49
 50(defn query-by-page
 51  "Gets query by page."
 52  [page]
 53  {:pre [(not (nil? page))]
 54   :post [(not (nil? %))]}
 55
 56  (if (contains? pages page)
 57    (let [pi (get pages page)]
 58      [app-query
 59       navbar-query
 60       (page-query (first pi) (second pi))])
 61    ;; Forces post-assert fail.
 62    nil))
 63
 64(defn page-info-by-props
 65  "Gets pages element for query via props."
 66  [props]
 67  {:pre [(not (nil? props))]
 68   :post [(not (nil? %))]}
 69
 70  (let [kw (first (filter #(s/starts-with? (str %) ":page+")
 71                          (keys props)))]
 72    (if (not (nil? kw))
 73      ;; Here kw has a :page+ type keyword.
 74      (let [kws (str kw)
 75            pg (str "/" (subs kws 6))
 76            pgi (if (= pg "/home") "/" pg)]
 77        (get pages pgi))
 78      ;; Forces post-assert fail.
 79      nil)))
 80
 81(defn keyword-by-props
 82  "Gets keyword for query via props."
 83  [props]
 84  {:pre [(not (nil? props))]
 85   :post [(not (nil? %))]}
 86
 87  (let [pi (page-info-by-props props)]
 88    (if (not (nil? pi))
 89      (first pi)
 90      ;; Forces post-assert fail.
 91      nil)))
 92
 93(defn factory-fcn-by-props
 94  "Gets factory function for query via props."
 95  [props]
 96  {:pre [(not (nil? props))]
 97   :post [(not (nil? %))]}
 98
 99  (let [pi (page-info-by-props props)]
100    (if (not (nil? pi))
101      (second (next pi))
102      ;; forces post-assert fail
103      nil)))
104
105;;------------------------
106;; Om-next root component.
107;;
108(defui App
109  static om/IQuery
110  (query [this]
111         (query-by-page "/"))
112
113  Object
114  (render [this]
115
116          (let [props (om/props this)
117                app-props (:app props)
118                {:keys [logged-in?]} app-props
119
120                navbar-props (:navbar props)
121                pkw (keyword-by-props props)
122                page-props (pkw props)]
123
124            (dom/div nil
125                     (dom/h4 nil (str "*** "(if logged-in? "LOGGED IN" "LOGGED OUT") "***"))
126
127                     (navbar navbar-props)
128                     ((factory-fcn-by-props props) page-props)))))
129
130;;-----------------
131;; Sets page.
132;;
133(defn set-page!
134  "Sets page via an Om Next set-query call. The resulting re-render of
135  App displays the new page."
136  [page]
137  {:pre [(not (nil? page))]}
138
139  (let [root (om/app-root reconciler)]
140    (when (and (not (nil? page))
141               (not (nil? root)))
142      (let [q (query-by-page page)]
143        (om/set-query! root
144                       {:query q})))))

Posts in this Series