Secretary Routing, Ident Query in om.next

154 views Asked by At

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.

0

There are 0 answers