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.

Reply via email to