9.1. Working with Templates

At a certain point, the controller hands over control to the view so that it can build the body of the response based on the appropriate layout, view templates, and partials. This can happen implicitly, when you let Rails process the default template for an action and request format, or explicitly by invoking the render method.

9.1.1. Rendering Templates

This section first reviews how default templates are determined and how it's possible to specify a non-default template to be used for the response to a request. It then takes a look at the available template types and standard engines that Rails bakes-in.

9.1.1.1. Default Templates

The view prominently enters into the picture whenever the response's body depends on a template. As you can imagine, this is the norm for HTML requests because their bodies are typically XHTML documents displayed through a browser. When render isn't called, the default template for a given action is rendered. As mentioned several times, the name of the default template that's going to be used is determined based on the action name and the format requested. If the controller is ExampleController, the view templates for its actions are conventionally stored in appviewsexample.

When processing the following action, index.html.erb is rendered by default:

# index.html.erb will be rendered by default
def index
  @books = Book.all
end

If the expected template is missing, a 500 Internal Server Error is raised and a "Template is missing" page is displayed in the browser.

The default template also depends on the format of the request:

def show
  @book = Book.find(params[:id])

  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :xml => @book }
    format.js   # show.js.rjs
  end
end

For instance, in the preceding snippet, the two highlighted lines indicate that the view should render the show.html.erb template for HTML requests and the show.js.rjs for JavaScript requests (for example, an Ajax call). The template engine used for the two is different as well. The first uses ERb to mingle XHTML and Ruby code, whereas the latter uses RJS so as to enable you to be able to write code in a Ruby DSL (Domain-Specific Language), which will be translated and rendered as JavaScript back to the user's browser.

9.1.1.2. Bypassing Templates with render

In its simplest form, render can be used to send a response that does not involve a template. You can render plain text:

render(:text => "Confirmed") # Sends the text without rendering the layout
render(:text => "<h1>OK!</h1>"), :layout =< true

XML, JSON, or JavaScript:

render(:xml => @article)
render(:json => @article)
render(:js => "alert('Hello from JavaScript'),")

to_xml and to_json will automatically be invoked for you in the first two lines. Call them explicitly with the :only, :except, :include, or :method arguments if you need to customize what should be included in the transformation of the object(s) to those two formats.

Though I've grouped more than one render call in each snippet to save space, remember that you should only render one per request.

or leave it blank (for an empty body):

render(:nothing)

This works if you only need to send headers in the response, but it's preferential to use the head method instead.

9.1.1.3. Rendering a Specific Template

When the render method is used explicitly, it can be leveraged to inform ActionView that a non-default template should be used. You can do this by rendering a template that's associated with a different action:

render(:action => "show")

or, when you need to render a template defined for an action within a different controller, by using the :template option:

render(:template => "sales/show")

or even from a specific file:

render(:file => "/path/to/a/shared/view/somewhere/show.html.erb")

You could also specify the template markup directly in the controller:

# Don't do this
render(:inline => "<% articles.each do |a| %><p><%= a.title %></p><% end %>")

And you could even do a render(:update) and pass a block of RJS code to it, so as to perform JavaScript-like updates directly in the controller.

NOTE

"Could" is the keyword here. You could, but that doesn't mean that you should. In fact, do not embed template markup or JavaScript-like page updates in your controller. Templates belong to the view layer, and the separation of concerns promoted by the MVC architectural pattern must be preserved.

Excluding bad practices like inline markup in the controller, a render :action, render :template, or render :file will tell the view to take over and formulate a proper response's body based on the specified template.

Partials

The view does not simply combine instance variables (for example, @article) with a single template (for example, show.html.erb). In fact, as seen in Chapters 5 and 6, the resulting document is usually the product of rendering a layout, which yields the rendering of the template at hand, which in turn can invoke the rendering of other templates and/or one or more partials. Partials, as explained earlier, are fragments of pages that can be used to improve the reusability and DRY adherence of the code.

It is also possible to have layouts that apply to partials. Unlike regular layouts that are scoped for an entire action and in many cases apply to a whole controller, these layouts are aimed at providing common code that wraps fragments of the view (that is, partials). These work in a very similar fashion to regular layouts; they contain a <%= yield %> where the partial should be rendered, and can be applied to a partial when this is called with the :layout option. For example:

<%= render :partial => "user", :layout => "admin", :locals =>
{ :admin => @user } %>

Partials can also be shared among views that are associated with different controllers. In such cases, it's conventional to place them within appviewsshared. When rendering these shared partials from within a template, you need to include the shared folder in the argument passed to render. Rails will be smart enough to understand that the last, and only the last, token is the name of the partial, while the rest is the path of a folder within appviews:

<%= render :partial => "shared/my_partial" %>

Check the documentation for ActionView::Partials for further details.


9.1.1.4. Communication with the Other Layers

Templates are able to access instance variables that have been defined within the controller as well as the flash. This is what the view is expected to do so that it can communicate with the controller. However, it's not limited to this alone. The view layer is capable of accessing a series of attributes you encountered in the previous chapter about controllers.

These attributes are controller, session, request, response, header, params, logger, and the debugger method (which triggers a debugging session). The view is even capable of interacting with the model directly. You could, for example, execute Book.all directly from the view.

As is often the case in the world of programming, the freedom that's provided to the programmer should not be abused. Yes, you could call the model directly from the view, but that would be a major "code smell" as well as a strong violation of the principle of separation of concerns. Likewise, with the exception of debugger, the aforementioned attributes are normally the domain of the controller, which handles them as required.

NOTE

Never query the model directly from the view. Use the controller instead.

Despite this due warning and premise, the ability to access them can come in handy when troubleshooting. In fact, Rails provides us with a debug helper, which dumps (using the YAML format) the object that is passed in argument. This turns out to be an easy way of inspecting the contents of the attributes mentioned earlier.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.70.247