Nesting widgets, pt. 1

In this Chapter nesting, render_widget, has_widgets
Github code simple-nesting
Versions 1.1
Next chapter

Since we moved our twitter widget to a namespace let’s learn how nesting widgets works. The panel widget should host the form to hide implementation details to the outside.

Public nesting

What sounds like a statement of facts is a simple way to setup a widget tree. Public nesting usually happens in the controller’s has_widgets block.

app/controllers/dashboard_controller.rb (snippet)
 1class DashboardController < ApplicationController
 2
 3  has_widgets do |root|
 4    root << panel = widget("twitter/panel", :twitter)
 5      panel << widget("twitter/form",  :tweet_form)
 6  end

Instead of adding the form to root, we add it to the panel widget (line 5).

The panel widget just got daddy! We can safely render the child inside the panel’s display view.

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

You can use #render_widget in widget views and in controller views. Check the API to learn more.

Now that we render the child in the widget, we should remove the rendering from the controller view. Otherwise, the form would be rendered twice, which is not what we want.

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

By moving the #render_widget call from the controller’s view into the panel’s view we remove responsibility from the controller – it shouldn’t care about the internals of the panel widget.

Private nesting

If the controller shouldn’t worry about the widget’s internals, why does it still have to setup a widget tree? That’s a legitimate complaint.

We can move the tree setup into the panel widget – that’s why widgets can have their own has_widgets blocks.

app/widgets/twitter/panel_widget.rb
 1class Twitter::PanelWidget < Apotomo::Widget
 2  
 3  has_widgets do
 4    self << widget("twitter/form", :tweet_form)
 5  end
 6  
 7  
 8  def display
 9    @tweets = Tweet.find(:all)
10    render
11  end
12  
13  def list(tweets)
14    render :locals => {:tweets => tweets}
15  end
16  
17end

The has_widget block in widget classes is executed in instance context, so you may access self and instance variables. In controllers, you may use the controller instance. Check the API to learn more.

This almost looks identical to the controller – however, there is no knowledge about the widget’s internal setup in the controller anymore. All it knows is “There’s a twitter panel widget” – the rest is hidden in the widget itself.

The controller now has a despeckled has_widgets block, again.

app/controllers/dashboard_controller.rb (snippet)
 1class DashboardController < ApplicationController
 2
 3  has_widgets do |root|
 4    root << panel = widget("twitter/panel", :twitter)
 5  end

Summary

A short process overview might help understanding what’s going on.

That’s nesting!

This is pretty cool. The form shows up in the panel. If only the list would update after we submit the form. Let’s explore how bubbling events can help here.


blog comments powered by Disqus