{{{
Days ago “I explained how to write a sidebar widget in Rails”:http://nicksda.apotomo.de/2010/11/lets-write-a-reusable-sidebar-component-in-rails-3/, using the view component framework “Cells”:http://github.com/apotonick/cells. This was fun.
Clicking a tag leads to a page reload, where the _Recent posts_ list *displays items matching the tag*, only. It worked great, however, each click needs a real HTTP request and a reload – *let’s use AJAX to handle that*.
h3. Rails and AJAX? Apotomo!
Widgets+Rails usually means you’re better off using “Apotomo”:http://apotomo.de , a new kid on the block. “Apotomo”:http://github.com/apotonick/apotomo is a web component framework for Rails, I will officially announce it in a separate post soon.
Let’s discuss how I *converted the _Recent posts_ cell into an interactive widget*. If that’s too brief, there is a detailed “in-depth tutorial”:http://apotomo.de/peters-guide.html here, just follow the links at _Learn_.
Check out “the repository of this example app”:https://github.com/apotonick/cells-blog-example/tree/with-apotomo *if you wanna play around*.
h3. Getting a cell interactive
We need the Apotomo gem, so I put it in the @Gemfile@.
gem 'rails', '3.0.1' gem 'cells' gem 'apotomo', "~>1.0" # ... and so on
I would never forget to run @bundle install@, Freddi!
In order to make a cell a widget, I inherit from @Apotomo::Widget@.
class PostsWidget < Apotomo::Widget def display @tag = param(:tag) @posts = @tag ? Post.tagged_with(@tag) : Post.recent render end def tag_cloud @tags = Post.tag_counts_on(:tags) render end end
Nothing really changed here, so far.
Note that deriving things from @Widget@ *doesn’t involve statefulness* or anything – it’s just a nestable cell now, being aware to Apotomo’s events. Right, Ryan?
h3. Widgets are cells
The assets layout slightly changed. Here’s how my @app/cells@ directory now looks.
|-- posts_widget | |-- display.html.haml | `-- tag_cloud.html.haml `-- posts_widget.rb
Some names changed, but *we still got our views in a separate directory*. Widgets also reside in @app/cells@, there’s no need to push another folder into Rails.
h3. Rendering a widget
After these changes, rendering the widget turns out to be fucking simple, too.
%html %head %title "The Incredible Cells Blog" %body #sidebar = render_widget 'sidebar-posts'
I just call @#render_widget@ where I used to call @#render_cell@ in the @application.html.haml@ layout.
The call referes to the widget _id_ which is defined in the controller’s widget tree. Let’s see how that works, so we can render the component.
h3. Messing up the widget tree
Usually, *widget trees are declared on the controller class layer*.
class PostsController < ApplicationController include Apotomo::Rails::ControllerMethods has_widgets do |root| root << widget(:posts_widget, 'sidebar-posts') end
Just include the necessary module and *setup the tree using the @has_widgets@ method*. Notice how I refer to the widget _class_ first, then I assign the _id_.
Wow- *this already renders the _Recent posts_ box!* The @#render_widget@ helper automatically invokes the @#display@ state of the widget.
h3. Triggering events
Clicking a tag still does a real request. As I want an AJAX call here, I change the @tag_cloud.html.haml@ view a bit.
#tag-cloud - tag_cloud(@tags, %w(css1 css2 css3 css4)) do |tag, css_class| = link_to tag.name, "", :class => css_class, 'data-event-url' => url_for_event(:tagClicked, :tag => tag.name) :javascript $("#tag-cloud a").click(function(e) { $.ajax({url: $(this).attr("data-event-url")}); return false; });
The first three lines didn’t change at all. However, two new steps involved here.
* First we *set an attribute in each clickable tag link*. The @#url_for_event@ computes some url needed to trigger the @:tagClicked@ event in Rails.
* Second a small jQuery script catches click events and *fires an AJAX request to the “_event url_”*. This is that unobstrusive Javascript everybody’s talking about.
What we need to do now is *catching the @:tagClicked@ event in the _ruby_ world* and update the item list in the browser.
Luckily, Apotomo saves us from any routing and dispatching.
h3. Catching events
As *Apotomo triggers the event in the widget tree*, the widget classes are one place to define observers.
class PostsWidget :filter def filter replace :state => :display end def display # ... and so on
I love Apotomo’s simple event system.
Again, two new steps.
* *Calling @responds_to_event@ sets the observer.* In case of fire, it invokes the @#filter@ state method.
* The @#filter@ state itself *just calls the @#display@ state* which collects posts and renders. The @#replace@ helper method simply *wraps the rendered view in a jQuery replace* statement. Note that you may do that yourself – it’s up to you to emit any Javascript.
Now, the tag filtering works in the browser.
*That’s all we need to write in order to have an AJAXed widget!* It simply works, without any magic at all.
h3. Apotomo is stable. Use it.
The framework we used here has been around for years but evolved from a “Model-driven architecture beast” into a small, stable widget framework based on Cells.
It is meant for RIA projects, dashboards, web desktops, well, “Apotomo”:http://github.com/apotonick/apotomo is here for helping you writing great widget-based web frontends, in a monolithic Rails environment.
}}}
That’s right!
LikeLike