I will preface this question by saying I am still very much a novice when it comes to Clojure/Script, so besides the very pointed question I will pose any general feedback about style, usage would be greatly appreciated.
I have been building a very simple application with om.next. For client side routing I have decided to use Secretary. My application displays a list of items, and the intention is that when you click on one of those items you can view the details. The implementation at present is via a simple href
on an anchor tag (e.g., an href
might look like /items/1
, where 1
is the id
). This is partially because you should be able to navigate to the details URL directly to see the same view. As simple as this sounds, I cannot for the life of me get this to work as desired.
First, let's look at the salient configuration to the reconciler (for brevity I have removed the implementation details of component render
throughout)...
(def init-data {:items [{:id 1
:title "stack overflow"
:description "hello, world."
:photos []}
{:id 2
:title "foo"
:description "bar"
:photos []}]})
(defui ListItem
static om/Ident
(ident [this {:keys [id]}]
[:items/by-id id])
static om/IQuery
(query [this]
[:id :title :description :photos]))
(defui Items
static om/IQuery
(query [this]
[{:items (om/get-query ListItem)}]))
(defmulti read om/dispatch)
(defmethod read :items
[{:keys [state query] :as env} key _]
(let [st @state]
{:value (om/db->tree query (get st key) st)}))
(def app-state (atom (om/tree->db Items init-data true)))
(def reconciler
(om/reconciler {:parser (om/parser {:read read})
:state app-state}))
The astute will see that I am trying to think with links, and this much seems to be working as I would expect. It is when I add this component, and try to use it behind a Secretary route, that everything breaks down...
; I tried other approaches, they failed too
(defui Item
static om/IQueryParams
(params [this]
{:id :not-found})
static om/IQuery
(query [this]
'[[:items/by-id ?id]]))
(defn render-component [component]
(let [app (gdom/getElement "app")]
(doto reconciler
(om/remove-root! app)
(om/add-root! component app)))))
(defroute item-path "/items/:id" [id]
(let [component Item]
; this is already less than ideal, as we know the id
; before the first render of the component
(render-component component)
(om/set-query! (om/class->any reconciler component)
{:id (js/parseInt id)})))
The instance of my Item
component does not receive the proper state in props
as specified in the query (it receives the entirety of app-state
). What is most perplexing to me is that if I execute the same query against app-state
manually in the REPL I get the right set of data back...
(get-in @app-state [:items/by-id 1])
; {:id 1, :title "stack overflow", :description "hello, world.", :photos []}
The only thing that has worked for me so far is to bypass the reconciler (creating the component instance myself), passing the query value from app-state
as props. But that means I can't mutate state via the reconciler, which is a new horrific wrinkle to an otherwise messy, and unkempt conundrum.
What am I missing here? While I have many ideas, the thing I'm most suspicious about is the initialization of app-state
via om/tree->db
.