I tried om.next, trying to extend the example given in Components, Identity and Normalization .
The code in the above example contains two lists that are backed up by data in two different ways in state. This example shows how data normalization makes it easy to update normalized objects found in different views. In the code below, I deleted the second list in the example below, as the behavior still shows up.
Working example
The example below works and shows:
List A
- John points: 4+
- Mary, points: 0 +
- Bob Points: 0 +
where + is the button that increases the point for a person.
(ns ui.core (:require [om.next :as om :refer-macros [defui]] [om.dom :as dom] [goog.dom :as gdom])) (def init-data {:list/one [{:name "John" :points 0} {:name "Mary" :points 0} {:name "Bob" :points 0}]}) ;; ----------------------------------------------------------------------------- ;; Parsing (defmulti read om/dispatch) (defn get-people [state key] (let [st @state] (into [] (map #(get-in st %)) (get st key)))) (defmethod read :list/one [{:keys [state] :as env} key params] {:value (get-people state key)}) (defmulti mutate om/dispatch) (defmethod mutate 'points/increment [{:keys [state]} _ {:keys [name]}] {:action (fn [] (swap! state update-in [:person/by-name name :points] inc))}) ;; ----------------------------------------------------------------------------- ;; Components (defui Person static om/Ident (ident [this {:keys [name]}] [:person/by-name name]) static om/IQuery (query [this] '[:name :points :age]) Object (render [this] (let [{:keys [points name] :as props} (om/props this)] (dom/li nil (dom/label nil (str name ", points: " points)) (dom/button #js {:onClick (fn [e] (om/transact! this `[(points/increment ~props)]))} "+"))))) (def person (om/factory Person {:keyfn :name})) (defui ListView Object (render [this] (let [list (om/props this)] (apply dom/ul nil (map person list))))) (def list-view (om/factory ListView {:key-fn ffirst})) (defui RootView static om/IQuery (query [this] (let [subquery (om/get-query Person)] `[{:list/one ~subquery}])) Object (render [this] (let [{:keys [list/one]} (om/props this)] (apply dom/div nil [ (dom/h2 nil "List A") (list-view one) ])))) (def rootview (om/factory RootView)) ;; wrapping the Root in another root (this fails) (defui AnotherRoot static om/IQuery (query [this] `[ ~@ (om/get-query RootView)]) Object (render [this] (dom/div nil (rootview (om/props this))))) (def reconciler (om/reconciler {:state init-data :parser (om/parser {:read read :mutate mutate}) })) (om/add-root! reconciler RootView (gdom/getElement "app"))
Problem: using AnotherRoot as the root component
However, when I change the RootView in the last line to AnotherRoot, for example:
(om/add-root! reconciler AnotherRoot (gdom/getElement "app"))
ui is still displayed, but when the button is pressed, the following error occurs:
Error: No queries exist for component path (ui.core/AnotherRoot ui.core/RootView ui.core/Person)
The error occurs from ( next.cljc: 1916 )
I do not understand the error. The request from AnotherRoot returns the same request as the RootView (however, you will receive a warning when the request is returned immediately, which makes sense), but the mediator does not seem to be able to understand the component visualization when the application state changes.
Corresponding dependencies should be:
[[org.clojure/clojure "1.9.0-alpha14"] [org.clojure/clojurescript "1.9.473"] [figwheel-sidecar "0.5.9"] [org.omcljs/om "1.0.0-alpha47"]]
Question
What is the general way to create nested components in om.next?