Drag&Drop with widgets 2.3 3.0 apotomo-1.0
In the we covered a simple tweeter. Now what if we’d like to dump an item? Besides that twitter doesn’t allow deleting (do they?), let’s start writing a trashbin widget.
When dropping an item the bin widget deletes it from the database. To stay in sync with our model, the list should also redraw. You’ll learn how to do that using event triggering in this lesson.
Dragging items
Drag&Drop with JavaScript is well done in jQuery UI. We need to include the new JS lib in our project.
Next, the listed items have to be draggable.
1- widget_div do 2 %ul 3 - for tweet in @tweets 4 %li{'data-id' => tweet.id} 5 = tweet.text 6 7 %p 8 What are you up to? 9 10 - form_tag "", "data-event-url" => url_for_event(:submit) do 11 = text_field_tag 'text' 12 13 = submit_tag "Tweet!" 14 15 :javascript 16 var form = $("##{widget_id} form"); 17 18 form.submit(function() { 19 $.ajax({url: form.attr("data-event-url"), data: form.serialize()}) 20 return false; 21 }); 22 23 $("##{widget_id} li").draggable({revert: "invalid"});
This happens in line 23 using the Draggable API.
Creating the trashbin widget
The bin widget is just a generator away.
$ rails generate apotomo:widget trashbin display -e haml
exists app/widgets/
create app/widgets/trashbin
exists test/widgets
create app/widgets/trashbin_widget.rb
create app/widgets/trashbin_widget/display.html.haml
create test/widgets/trashbin_widget_test.rb
The TrashbinWidget is bloody simple.
Our standard view draws the bin image (that I’ve stolen from somewhere).
In line 5 we define the image from line 1 as Droppable.
On drop, we send an AJAX request to the widget’s :trash event url appending the dropped item’s id.
Catching the dropped
Remember that the trashbin widget handles trash events.
Simply put, when the bin sees a trash event, it will invoke its trash state. Notice how I can omit the :with => ... portion if the names of event and triggered method are equal.
In line 12 we update the current widget’s div on the page with the display view.
Triggering events
What’s that trigger statement in line 10?
Well, we simply inform the outer world that we altered the tweets table – since we ruthlessly deleted an item.
Bubbling events
According to Apotomo’s bubbling event concept, the tweetDeleted event will bubble up to root. Why not instruct the list widget to catch it and update itself?
1class TwitterWidget < Apotomo::Widget 2 responds_to_event :submit, :with => :process_tweet 3 4 def display 5 @tweets = Tweet.find(:all) 6 render 7 end 8 9 def process_tweet(evt) 10 Tweet.new(:text => evt[:text]).save 11 12 @tweets = Tweet.find(:all) # this is wet! 13 replace :view => :display 14 end 15 16 def redraw 17 replace :state => :display 18 end 19end
We just add the redraw state (line 16) which invokes display and replaces the widget content in the browser (line 17).
Since events bubble up, we need to attach an event handler to root. That can be done in the controller.
Line 7 instructs root to call updating on our twitter widget when encountering the respective event.
However, putting observer instructions in the controller is not very clean and breaks widget encapsing, since the controller knows details about the widgets’ internals.
There are two ways to make this suck less.
- Have a container that wraps both widgets.
- Use the
after_addhook to add an observer.
Both approaches use Apotomo’s event bubbling.

