While Om improves significantly over plain React, there's 2 related problems 
I've been running into that I'd like to hear other opinions on.
1) Difficulty of passing children (as in React's props.children)
2) Custom component construction syntax does not mirror DOM element 
construction syntax

This is easier for me to explain with code, so I wrote up an example. Let's 
assume the only way I can get something vertically centered is with a couple 
wrapper divs, and I want to make that solution reusable with a component.

;; What I would ideally like to write is this:
(defn vertically-center 
  "Vertically centers its children"
  [data owner]
  (reify
    om/IRender
    (render [_]
      (dom/div #js {:className "vertical-center-outer-wrapper"}
               (dom/div #js {:className "vertical-center-inner-wrapper"}
                        ;; include the component's passed-in children,
                        ;; be they dom elements or custom components
                        (aget owner "children"))))))

;; elsewhere, in another component's render fn (still in my idealized world):
(dom/div #js {:id "whatever"}
         ;; construct without build.
         ;; Syntax for constructing components is very similar to syntax for
         ;; constructing dom elements.
         ;; It might even by nice to be able to omit the cursor as well,
         ;; when it isn't needed.
         (vertically-center some-cursor
                            ;; pass in children directly
                            (dom/p #js {:className "my-vertically-centered-p"}
                                   "Some Text")))

;; The only way I know to accomplish that in the real world is the following:
(defn vertically-center [data owner]
  (reify
    ;; Need to use :state to account for the possibility that the children
    ;; list will change over the lifetime of this component
    om/IRenderState
    (render-state [_ {:keys [children]}]
      (dom/div #js {:className  "vertical-center-outer-wrapper"}
               ;; I can include children without apply because it's a js array
               (dom/div #js {:className "vertical-center-inner-wrapper"} 
children)))))

;; elsewhere, in another component's render fn
(dom/div #js {:id "whatever"}
         ;; need to use build for custom components
         (om/build vertically-center some-cursor
                   ;; This is wordier than passing children directly.
                   {:state
                    {:children #js [(dom/p #js {:className 
"my-vertically-centered-p"}
                                           "Some Text")]}}))


I've thought up a couple possible half-solutions:

1) A macro for component definition that turns calls to the component into 
calls to om/build the component. I tried this, and found I lack the macro 
wisdom to make it work, but what I came up with is this: 

(defmacro nobuild [body]
  (let [macro-name (nth body 1)
        component-name (symbol (str (name macro-name) "*"))]
    ;; add a macro to the compiler's runtime that is in the same
    ;; namespace as the calling code
    ;; and which expands to call om.core/build
    (intern *ns*
            (with-meta macro-name {:macro true})
            (fn [& args]
              `(om.core/build ~component-name ~@args)))
    ;; The actual expansion just replaces the component's name
    ;; with the one generated here.
    (~(first body)
      ~@(cons component-name (rest (rest body))))))

;; Then call it like this:
(nobuild (defn my-component [data owner] (reify etc...)))
;; and calls to (my-component) could expand into calls to (om/build 
my-component)
;; This probably isn't such a good solution.

2) This one would require a change to Om, but om/build could take any number of 
arguments and treat any after the second as children, except the last one is an 
optional map:

(defn build  
  ([f cursor & children-and-maybe-a-map]
    ;; I'm not sure what would go here.
    ))

I appreciate any input or advice on this topic.

-Tom

-- 
Note that posts from new members are moderated - please be patient with your 
first post.
--- 
You received this message because you are subscribed to the Google Groups 
"ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/clojurescript.

Reply via email to