Composed widgets 2.3 3.0 apotomo-1.0

Being real web components, Apotomo widgets can be nested. This allows building more complex widget trees where widgets are put into containers.
We need a new feature: Tweets should be markable as “loved” if we like them.
Clicking on the heart icon flags the respective Tweet row as loved and redraws the list item. Let’s make the TwitterWidget a container where each loveable tweet item is a separate child widget.
Making tweets loveable
First add the new field to the tweets table.
rails generate migration add_loved_flag_to_tweet loved:boolean
And execute the migration
rake db:migrate
Now it’s time for another widget, yeah!
Creating a new widget
The generator strikes back – we call it TweetWidget as it displays one item, only.
rails generate apotomo:widget tweet display -e haml
exists app/widgets/
create app/widgets/tweet_widget
exists test/widgets
create app/widgets/tweet_widget.rb
create app/widgets/tweet/display.html.haml
create test/widgets/tweet_widget_test.rb
The new class is pretty straight-forward.
Somebody has to pass in the Tweet instance we have to display – that’s usually the container’s job.
Containing some JavaScript capable of improvement, the view is still simple. I never said I’m good at JavaScript.
1%li{:id => widget_id, 'data-id' => @tweet.id, 'data-event-url' => url_for_event(:heart)} 2 = @tweet.text 3 4 - if @tweet.loved? 5 = image_tag "heart.png" 6 - else 7 = image_tag "heart-faded.png" 8 9:javascript 10 $("##{widget_id} img").click(function() { 11 $.ajax({url: $("##{widget_id}").attr("data-event-url") + "&id=" + 12 $("##{widget_id}").attr("data-id")}) 13 });
Basically, if the heart is clicked, an AJAX request is issued (line 10-12) to the url provided by #url_for_event (line 1). Also, we append the id of the loved Tweet object. I never said I’m good at JavaScript.
Embedding widgets into containers
A container widget is no special thing, it’s just adding children to itself and rendering ’em. The old TwitterWidget will be the container.
So far, only #display changed. For each tweet it appends a new TweetWidget instance passing in the :tweet as parameter (line 11).
It is important to understand that you can use the composing methods like #<< or #remove! not in controllers has_widget blocks only, but in widgets, too.
Rendering children
To render widgets within state views you may use #render_widget.
Nothing really changed here as well – except that we simply call children (line 2). Pretty handy.
This looks good. If we click a heart icon… an AJAX request is triggered but brings us back an exception.
Source "tweet-26" non-existent.
Well, we are in a stateless setup, so naturally in the triggered state the container widget forgets all the children we added earlier. The problem: when we trigger an event from a tweet widget (by clicking the heart), Apotomo tries to find this widget instance to start bubbling the event. But it’s already gone as it didn’t survive the request.
Lucky us we already read about that.
Statefulness in a stateless world
The has_widgets block in widgets is our substitution for the lack of statefulness here (hey, you chose to derive from Widget instead of StatefulWidget!) – it is run every time the widget is instantiated.
1class TwitterWidget < Apotomo::Widget 2 responds_to_event :submit, :with => :process_tweet 3 4 after_add do 5 root.respond_to_event :tweetDeleted, :with => :redraw, :on => widget_id 6 end 7 8 has_widgets do 9 for t in Tweet.find(:all) 10 self << widget(:tweet, "tweet-#{t.id}", :tweet => t) 11 end 12 end 13 14 15 def display 16 render :layout => "portlet"
Instead of adding the kids in an explicit state (that was in #display before) we put the setup on the class layer (line 8-12).
Clicking the heart now works… nothing happens, though, cause we didn’t setup an event handler.
Catching the heart beat
The click should be processed in the TweetWidget which fired it as well.

We already know that shit. First create an observer (line 2). In #toggle we, well, toggle the loved state of the desired tweet and replace the content with a re-rendered display view (line 9-14).
And voilà – clicking the heart icon updates the tweet.

