Thanks for the reply Colin, I did read the entire thread but I'm still
don't have a solution...

My case is a bit different than yours, because yours will just render in
the end, in my case I need to do more manual stuff (for rending on the
canvas context) and I'm trying to rely on the React lifecycle methods to do
some caching on the rendering (the idea is to render off-canvas when data
changes for that component, and parent components will ask childs to render
itself on the canvas, and they can do that from the off-screen cached).

My current major issue is that I can't find a way to get the children of a
component, on React that would be done by simply props.children, but I
can't find a way to do the same on Om (tried to look at state, props, owner
and this, but not found anything there.

Here is some code that I'm trying to get to work:

(defprotocol IDraw
  (draw [this ctx]))

(defn surface [data owner]
  (let [redraw (fn []
                 (let [ctx (-> (om/get-node owner) (.getContext "2d"))]
                   (doseq [child (GET_CHILDS_SOMEHOW)]
                     (if (satisfies? IDraw child)
                       (draw child ctx)))))]
    (reify
      om/IDisplayName
      (display-name [_] "Surface")
      om/IDidMount
      (did-mount [_] (redraw))
      om/IDidUpdate
      (did-update [_ _ _] (redraw))
      om/IRender
      (render [_]
        (apply dom/canvas #js {:width 300 :height 300} data)))))

(defn surface-square [{:keys [width height x y]} owner]
  (reify
    om/IDisplayName (display-name [_] "Square")
    ; caching rendering on mount
    om/IDidMount
    (did-mount [_]
      (om/set-state! owner :rendered (render-off-screen #(.fillRect % x y
width height))))
    ; the update cache on update is missing for now, but I think you got
point
    IDraw
    ; draw the cached version
    (draw [_ ctx] (.drawImage ctx (om/get-state owner :rendered)))
    om/IRender
    (render [_] nil)))

(om/build surface
  [(om/build surface-square {:x 10 :y 10 :width 20 :height 20})])

So, the code above doesn't work for a couple of reasons:

1. I didn't found yet a way to actually get the children components (but on
React inspector it shows they are there)
2. Besides I like the idea of having a protocol for drawing, it will not
work since this reified object is not actually used, instead it's wrapped
by Om, so I guess this protocol idea will never really work (at least this
is what I understood after reading the sources, please tell me if I'm wrong
about it)...

-----------------------

I paused writing this post and now I got a working solution, I'll leave the
previous text since it may be useful to follow the though process, but here
is a working solution using multimethods:

(defmulti render :type)

(defn render-off-screen [f w h]
  (let [canvas (doto (.createElement js/document "canvas")
                 (aset "width" w) (aset "height" h))
        ctx (.getContext canvas "2d")]
    (f ctx)
    canvas))

(defn clear-canvas [canvas]
  (doto (.getContext canvas "2d")
    (.clearRect 0 0 (.-width canvas) (.-height canvas))))

(defn surface [data owner]
  (reify
    om/IDisplayName
    (display-name [_] "Surface")
    om/IDidMount
    (did-mount [_])
    om/IWillUpdate
    (will-update [_ _ _] (clear-canvas (om/get-node owner)))
    om/IRender
    (render [_]
      (apply dom/canvas #js {:width 300 :height 300} (map #(render (assoc %
:surface owner)) data)))))

(defn render-to-cache [owner render-fn w h]
  (om/set-state! owner :rendered (render-off-screen render-fn w h)))

(defn render-cached [owner]
  (let [ctx (-> (om/get-props owner :surface) (om/get-node) (.getContext
"2d"))]
    (.drawImage ctx (om/get-state owner :rendered) 0 0)))

(defn surface-square [{:keys [width height x y] :as data} owner]
  (let [render (fn [ctx] (.fillRect ctx x y width height))]
    (reify
      om/IDisplayName (display-name [_] "Square")
      om/IDidMount
      (did-mount [_]
        (render-to-cache owner render width height)
        (render-cached owner))
      om/IDidUpdate
      (did-update [_ props _]
        (if (not= props data) (render-to-cache owner render width height))
        (render-cached owner))
      om/IRender
      (render [_] nil))))

(defmethod render :square [d] (om/build surface-square d))

(om/build surface
  [{:type :square :x 10 :y 10 :width 20 :height 20}])

I think that will work, I would like to be able to reference the parent
component owner without having to explicitly send it on the props (which
I'm currently doing on the Surface render).

Besides that I would like any suggestions on improvements for this, at
least now it's working :)

Thanks.

On Thu, Mar 19, 2015 at 8:57 AM Colin Yates <[email protected]> wrote:

> Hi, I asked this before and the short answer is *not* by passing in
> realised components in another another component's state as you have
> done (which was almost my first attempt).
>
> The consensus was to use multi-methods. If you can't then you pass the
> child state and the child fn through to the container which then
> invokes (om/build child-fn child-state).
>
> https://groups.google.com/d/msg/clojurescript/6HfwarWsFfI/lvRbRFAeiV0J
> has a lot more detail.
>
> On 19 March 2015 at 11:53, Wilker <[email protected]> wrote:
> > Hi,
> >
> > I'm creating a component here that will actually render on canvas, to be
> > more specific I wanna have a tree definition that end's up rendering on
> > Canvas (kind of the react-canvas project, but on Om, and for other
> > purposes).
> >
> > In React is more clear how to write things like this, you can do:
> >
> > <Surface>
> >   <Layer>
> >     <Rectangle width="100" height="100" />
> >   </Layer>
> > </Surface>
> >
> > And on react you have the children props and that stuff that can be use
> to
> > "integrate" those components.
> >
> > But how do I accomplish a similar thing with Om?
> >
> > The farthest that I was able to got was an idea like this:
> >
> > (om/build surface
> >   [(om/build layer [(om/build square {:width 100 :height 100}]])
> >
> > This is just an "idea" on how to nest components that I came up, but it's
> > not really working...
> >
> > Ideally I would like for the child node to be able to access the parent
> in
> > order to call methods on the canvas (that's on the surface component) to
> > render things.
> >
> > How can I manage this kind of components with Om?
> >
> > Thanks.
> >
> > --
> > 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.
>
> --
> 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.
>

-- 
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