om-next-router-example icon indicating copy to clipboard operation
om-next-router-example copied to clipboard

Child components with IQueryParams

Open dsvensson opened this issue 9 years ago • 1 comments

It's pretty common to have child components with parameters. I've attemted to get this working loosly based on your layout without any luck. I pass down a function to the child components, so that they can add an onClick wherever they want, that transact the root component with the new route, similar to your route/update but also include params. I then set the child component query with parameters in the root query by extracting the query via route->query, and patching the default params to the ones passed on with the route/update call. This is however not enough, as when the components factory function is called in the render function, it will be created with the default IQueryParams, rather than the ones used in the root query. This will result in the reconciler's send function getting the correct query params for the child component query, but when reaching the render function of the child component, the params will be the default ones, and thus it will not be able to access the data it has been parametrized to use.

dsvensson avatar Jan 25 '16 22:01 dsvensson

My current workaround for this is as follows, I hope it illustrates my problem, and that there's a much better way to accomplish the same.

(defmethod readf :category
  [{:keys [state] :as env} key params]
  (let [st @state]
    (if-let [value (get-in st [key (:section params) (:view params)])]
      {:value value}
      {:remote true})))

(defmethod mutatef 'session/set-route
  [{:keys [state]} _ {:keys [name args]}]
  {:action #(swap! state assoc
                   :session/route name
                   :session/params args)})

(defui Category
  static om/IQueryParams
  (params [this]
          {:item (om/get-query Item)
           :section nil
           :view nil})
  static om/IQuery
  (query [this]
         '[({:category ?item} {:section ?section :view ?view})])
  Object
  (componentWillMount [this]
                      (let [{:keys [section view]} (:params (om/get-computed this))]
                        (om/update-query! this #(update %1 :params conj {:section section :view view}))))
  (render [this]
         (html
           (let [{:keys [category]} (om/props this)
                 {:keys [update-route]} (om/get-computed this)
                 open-item #(update-route :programme {:programmeid %1})]
          ...))

(defui App
  static om/IQuery
  (query [this]
         (let [subquery (route->query :categories)]
           [:session/route :session/params {:child subquery}]))
  Object
  (componentWillUpdate [this next-props next-state]
                       ;; horror to be cleaned up, but need to hack in params, and
                       ;; re-attach the original metadata to the updated query for
                       ;; the components to render correctly.
                       (let [query (route->query (:session/route next-props))
                             params (:session/params next-props)
                             ast (om/query->ast query)
                             parametrized (om/ast->query (assoc-in child-ast [:children 0 :params] params))]
                         (om/set-query! this {:query [:session/route
                                                      :session/params
                                                      {:child (with-meta parametrized (meta query))}]})))
  (update-route [this target params]
                (om/transact! this `[(session/set-route {:name ~target :args ~params})]))
  (render [this]
          (let [{:keys [session/route session/params child]} (om/props this)]
            (html [:div
                   ((route->factory route)
                    (om/computed child
                                 {:update-route (fn [tgt arg] (.update-route this tgt arg))
                                  :params params}))]))))

dsvensson avatar Jan 25 '16 23:01 dsvensson