3

I am currently trying to learn om.next.

This is the code that I have:

(ns hlearn.core
  (:require [goog.dom :as gdom]
            [om.next :as om :refer-macros [defui]]
            [om.dom :as dom]
            [sablono.core :as html :refer-macros [html]]))

(enable-console-print!)

(def app-data
  (atom {:user ""
         :main-menu {:selected :home}}))

;; -------------------------------------------------------------------------
;; Parsing

(defmulti read om/dispatch)

(defmethod read :selected
  [{:keys [state]} _ _]
  {:value (get-in @state [:main-menu :selected])})

;; -------------------------------------------------------------------------
;; Components

(defui MainMenu
  static om/IQuery
  (query [this]
         [:selected])
  Object
  (render [this]
          (let [{:keys [selected]} (om/props this)]
            (println (= selected :home)))))

(def main-menu (om/factory MainMenu))

(defui RootView
  Object
  (render [this]
    (println "Render RootView")
    (main-menu)))

(def reconciler
  (om/reconciler
   {:state  app-data
    :parser (om/parser {:read read})}))

(om/add-root! reconciler
  RootView (gdom/getElement "app"))

My goal here is that the component MainMenu has to write true on the console (currently writes false).

Since the read function should return {:value :home} (value on the app-state), therefore (= selected :home) should be true.

In practice, MainMenu writes false on the console, because selected has the value nil.

cfrick
  • 35,203
  • 6
  • 56
  • 68
lhahn
  • 1,241
  • 2
  • 14
  • 40

2 Answers2

2

Turns out that the RootView component that is rendered on (om/add-root!) must provide the query for all the application.

In this case RootView must provide a query as well, and pass the selected key to the MainMenu component.

(def app-data
  (atom {:user ""
         :menu {:selected :home}}))

(defui RootView
  static om/IQuery
  (query [this]
    `[{:menu (om/get-query MainMenu)}])
  Object
  (render [this]
    (let [{:keys [menu]} (om/props this)]
      (println "Render RootView")
      (main-menu menu))))

Also, the reader function dispatches on the :menu key, instead of the :selected key.

(defmethod read :menu
  [{:keys [state]} _ _]
  {:value (get-in @state [:menu])})

The rest of the code remains the same.

lhahn
  • 1,241
  • 2
  • 14
  • 40
1

In om.next, all queries have to compose to the root component, as you discovered. The root component is assigned its props based on what the read function returns when it processes the root query.

If any subcomponents have a query, the props requested by that query should be passed down as the first argument the subcomponent's factory method. This was the second piece you were missing.

There isn't currently any warning or error in Om if you query for something in a subcomponent that isn't ultimately passed down by its parent. So you end up destructuring a nil value for the key in the subcomponent if you forget to pass down props.

egracer
  • 362
  • 3
  • 10