4.3 Render unto View
By the way, make sure you reload the console when you make changes—it doesn’t react to changes in sourcecode automatically. The easiest way to reload the console is simply to type
reload!
. But be aware that anyexisting instances of Active Record objects that you’re holding on to will also need to be reloaded (using theirindividual reload methods). Sometimes it’s simpler to just exit the console and start it up again.
4.3.1 When in Doubt, Render
Rails knows that when it gets a request for the index action of the demo controller, what really matters is handing something back to the server. So if there’s no index action in the controller file, Rails shrugs and says, “Well, let’s just assume that if there were an index action, it would be empty anyway, and I’d just render index.html.haml. So that’s what I’ll do.”
class DemoController < ApplicationController
def index
end
end
What you learn from seeing the empty action is that, at the end of every controller action, if nothing else is specified, the default behavior is to render the template whose name matches the name of the controller and action, which in this case means app/views/demo/index.html.haml.
In other words, every controller action has an implicit render command in it. And render is a real method. You could write the preceding example like this:
def index
render `demo/index`
end
You don’t have to, though, because it’s assumed that it’s what you want, and that is part of what Rails people are talking about when they discuss convention over configuration. Don’t force the developer to add code to accomplish something that can be assumed to be a certain way.
4.3.2 Explicit Rendering
If a controller action doesn’t want to render its default template, it can render a different one by calling the render method explicitly. Any template file in the app/views
directory tree is available. (Actually, that’s not exactly true. Any template on the whole system is available!)
4.3.3 Rendering Another Action's Template
A common reason for rendering an entirely different template is to redisplay a form, when it gets submittedwith invalid data and needs correction.
class EventController < ActionController::Base
def new
# This (empty) action renders the new.html.haml template, which
# contains the form for inputting information about the new
# event record and is not actually needed.
end
def create
# This method processes the form input. The input is available via
# the params hash, in the nested hash keyed to :event
@event = Event.new(params[:event])
if @event.save
# ignore the next line for now
redirect_to dashboard_path, notice: "Event created!"
else
render action: 'new' # doesn't execute the new method!
end
end
end
Note that the template itself doesn’t “know” that it has been rendered by the create action rather than the new action. It just does its job: It fills out and expands and interpolates, based on the instructions it contains and the data (in this case, @event) that the controller has passed to it.
4.3.4 Rendering a Different Template Altogether
In a similar fashion, if you are rendering a template for a different action, it is possible to render any template in your application by calling render with a string pointing to the desired template file. The render method is very robust in its ability to interpret which template you’re trying to refer to.
render template: '/products/index.html.haml'
# It’s not necessary to pass a hash with :template, because it’s the default option.
render '/products/index.html.haml'
render 'products/index.html.haml'
render 'products/index.html'
render 'products/index'
render 'index'
render :index
The
:template
option only works with a path relative to the template root (app/views, unless you changed it, which would be extremely unusual).
4.3.5 Rendering a Partial Template
Another option is to render a partial template (usually referred to simply as a partial). Usage of partial templates allows you to organize your template code into small files. Partials can also help you to avoid clutter and encourage you to break your template code up into reusable modules.
There are a few ways to trigger partial rendering. The first, and most obvious, is using the :partial
option to explicitly specify a partial template. Rails has a convention of prefixing partial template file names with an underscore character, but you never include the underscore when referring to partials.
render partial: 'product'#rendersapp/views/products/_product.html.haml
render partial: 'shared/product'#rendersapp/views/shared/_product.html.haml
render partial: @product
render @product
render 'product'
4.3.6 Rendering Inline Template Code
Occasionally, you need to send the browser the result of translating a snippet of template code, too small to merit its own partial. I admit that this practice is contentious, because it is a flagrant violation of proper separation of concerns between the MVC layers.
Rails treats the inline code exactly as if it were a view template. The default type of view template processing is ERb, but passing an additional :type option allows you to choose Haml.
render inline: "%span.foo#{@foo.name}", type:"haml"
4.3.7 Rendering Text
What if you simply need to send plain text back to the browser, particularly when responding to Ajax and certain types of web service requests?
render text: 'Submission accepted'
Unfortunately, if you don’t pass an additional :content_type
option, Rails will default the response MIME
type to text/html
, rather than text/plain
. The solution is to be explicit about what you want.
render text: 'Submission accepted', content_type: 'text/plain'
4.3.8 Rendering Other Types of Structured Data
4.8.1 :json
JSON is a small subset of JavaScript selected for its usability as a lightweight data-interchange format.
render json: @record
As long as the parameter responds to to_json
, Rails will call it for you, which means you don’t have to call
it yourself with ActiveRecord objects.
Any additional options passed to render :json
are also included in the invocation of to_json
.
render json: @projects, include: tasks
Additionally, if you’re doing JSONP, you can supply the name of a callback function to be invoked in the
browser when it gets your response. Just add a :callback
option with the name of a valid JavaScript method.
render json: @record, callback: 'updateRecordsDisplay'
4.8.2 :xml
Active Record also has built-in support for conversion to XML, as in the following example:
render xml: @record
As long as the parameter responds to to_xml
, Rails will call it for you, which means you don’t have to call it
yourself with ActiveRecord objects.
Any additional options passed to render :xml
are also included in the invocation of to_xml
.
render xml: @projects, include: :tasks
4.3.9 Rendering Nothing
On rare occasions, you don’t want to render anything at all. (To avoid a bug in Safari, rendering nothing actually means sending a single space character back to the browser.)
head :unauthorized
The head
method allows you to return a response with no content, and a specific status code. You could achieve the same result as the above code snippet, by calling render nothing: true
and explicitly providing a status.
render nothing: true, status: 401
The head
method also accepts an options hash, that is interpreted as header names and values to be included with the response. To illustrate, consider the following example which returns an empty response with a status of 201, and also sets the Location header:
head :created, location: auction_path(@auction)
4.3.10 Rendering Options
Most calls to the render method accept additional options. Here they are in alphabetical order.
:content_type
All content flying around the web is associated with a MIME type. For instance, HTML content is labeled with a content-type of text/html
. However, there are occasions where you want to send the client something other than HTML. Rails doesn’t validate the format of the MIME identifier you pass to the :content_type
option, so make sure it is valid.
:layout
By default, Rails has conventions regarding the layout template it chooses to wrap your response. The :layout
option allows you to specify whether you want a layout template to be rendered if you pass it a boolean value, or the name of a layout template, if you want to deviate from the default.
render layout: false # disable layout template
render layout: 'login' # a template app/views/layouts is assumed
:status
The HTTP protocol includes many standard status codes4 indicating a variety of conditions in response to a client’s request. Rails will automatically use the appropriate status for most common cases, such as 200 OK for a successful request.