I've been playing around with Om recently, and started down a rabbithole
towards a drag-drop implementation.
In my (toy) application, I have a couple of lists of items which I want to drag
between. Before stumbling on Om and React, I was using a goog.fx.DragListGroup,
and listening to the events. But binding data back to sub-components of the
app was starting to look onerous.
Om to the rescue!
The lists are in app-state and mutations on them cascade nicely to the
sub-components. Great.
So my first thought was to try to integrate DragListGroup somehow (after all -
that code is already written). Unfortunately, React doesn't like DOM
manipulation outside it's lifecycle (it's possible, but much harder to do in
idiomatic Om).
Fortunately, David Nolen threw a sortable example together.
Building on that example, I now have my "master app" component creating some
core.async channels, which it then passes down to sub-components. These
components can register for drag-events within their visible bounds.
So here's my problem : when the bounds of a component change (i.e. it get's
re-sized), the existing channel still only delivers events to the old bounds.
I 'solved' this by untapping the old channel, creating a new channel with a
filter for the new bounds and listening to it.
This seems .. unwieldy.
; state is either current or previous state , depending on whether this
; is called from did-update or did-mount.
(defn listen-bounds [owner state opts ref-node]
(when-let [container (om/get-node owner ref-node)]
(let [ dims (-> container gstyle/getSize gsize->vec)
disperser (disperser opts)
drag-chan (:drag-target-chan state)
new-bounds (bounds container)
track-fn (fn [evt] (om/set-state! owner :last-evt evt))
]
(if (= (:bounds state) new-bounds)
(do
(if (nil? drag-chan)
(create-listener disperser new-bounds track-fn)
drag-chan)
)
(do
(when-not (nil? drag-chan)
(untap disperser drag-chan)
(close! drag-chan))
(om/set-state! owner :bounds new-bounds)
(let [new-drag-chan (create-listener disperser new-bounds track-fn)]
(om/set-state! owner :drag-target-chan new-drag-chan)
new-drag-chan)
)))))
(defn directed-event-chan [bounds]
(filter>
(bound-filter bounds)
(chan)))
(defn create-listener [disperser bounds f]
(let [drag-target-chan (directed-event-chan bounds)]
(tap disperser drag-target-chan)
(go (while
(when-let [event (<! drag-target-chan)]
(f event)
event
)))
drag-target-chan
))
So two questions :
1. I couldn't tell if untapping a channel also effectively closes it. Is
(close! ..) necessary here?
2. Is there some way to avoid this churn? Would using an atom in
(directed-event-chan [@bounds] get me a dynamic filter?
As an aside : A lot of the core-async tutorials use (go (while true ...
I haven't gotten into the implementation, but if all channels in a go loop are
closed, is that code still taking up memory somewhere?
I erred on the safe side (perhaps at the expense of readability) by using
(go (while (when-let ... ) and returning the value of the last read from the
channel. When the channel returns nil upon close, this is guaranteed to exit.
--
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.