Namespacing widgets

In this Chapter refactoring, namespaced widgets, generator
Github code twitter-namespaced
Versions
Next chapter

This guide is for you, Kevin!

We decided to extract the form behaviour from our twitter widget into a separate component. We will get two widgets then, one listing items while hosting the form, and the form itself.

It’s good choice to put these connected components into a namespace.

Generating namespaced widgets

Let the generator create a namespaced form widget.

rails g apotomo:widget Twitter::Form display -e haml
    create  app/widgets/twitter/form_widget.rb

Cool, the generator pushes the widget into a sub-directory. That’s cause we provided the namespaced widget name Twitter::Form.

Looking at the new widget class we figure it’s inside a module.

app/widgets/twitter/form_widget.rb
1class Twitter::FormWidget < Apotomo::Widget
2  
3  def display
4    render
5  end
6
7end

Namespacing?

So, namespacing is basically pushing widget classes into the namespace module and packing the assets into a subdirectory.

After moving the TwitterWidget to Twitter::PanelWidget manually things look like this:

Original file After namespacing
app/widgets/twitter_widget.rb app/widgets/twitter/panel_widget.rb
app/widgets/twitter/display.html.haml app/widgets/twitter/panel/display.html.haml
app/widgets/twitter/form.haml app/widgets/twitter/form/display.html.haml
- app/widgets/twitter/form_widget.rb

Refactoring

We need to move some code portions from the original TwitterWidget to the new Twitter::FormWidget.

After a bit of refactoring, the form widget looks like this.

app/widgets/twitter/form_widget.rb
 1class Twitter::FormWidget < Apotomo::Widget
 2  responds_to_event :submit
 3  
 4  def display
 5    @tweet = Tweet.new
 6    render
 7  end
 8
 9  def submit(evt)
10    @tweet = Tweet.new
11    
12    if @tweet.update_attributes(evt[:tweet])
13      replace :state => :display
14    else
15      replace :view => :display
16    end
17  end
18
19end

That’s old code we already know, in a new shell. Three things changed here, so far.

The form view changed a bit, too.

app/widgets/twitter/form/display.html.haml
 1= widget_div do
 2  
 3  What are you up to?
 4 
 5  - form_for :tweet, :url => url_for_event(:submit), :remote => true do |f|
 6    
 7    = f.error_messages
 8    = f.text_field 'text'
 9   
10    = submit_tag "Tweet!"

We simply wrap the form in a #widget_div so we can use #replace (line 1).

Another step for now is updating the panel display view – we don’t want to render the #form state anymore, since this is a discrete stand-alone widget now.

app/widgets/twitter/panel/display.html.haml
1- widget_div do
2  
3  = render({:state => :list}, @tweets)
4  

Using namespaced widgets

The widget tree in the DashboardController needs a bit of updating to show our namespaced widgets.

app/controllers/dashboard_controller.rb
 1class DashboardController < ApplicationController
 2
 3  has_widgets do |root|
 4    root << widget("twitter/panel", :twitter)
 5    root << widget("twitter/form",  :tweet_form)
 6  end
 7
 8  def index
 9  end
10end

Here, we add two widgets to root. Notice how I refer to the namespaced class names by separating the module and class with a / as in twitter/panel.

The panel widget gets an explicit id :twitter (line 4). The second widget will be named :twitter_form explicitely (line 5).

Understanding the id naming is especially important when it comes to rendering.

app/views/dashboard/index.html.haml
1%h1 My Dashboard
2
3#dashboard
4  .column
5    = render_widget :twitter
6    
7    = render_widget :tweet_form

That works, both widgets are rendered. However, the tweets list is not updated after a successful submission.

Well, of course it doesn’t update!? How would the panel know about updates from the separated form widget? We will use nesting and bubbling events in the next chapters to accomplish that! Yay!


blog comments powered by Disqus