Rendering HTML with Views

Rendering HTML

With our route properly handling requests to the action /hello_world, let's change our action to render some HTML instead:

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  def hello_world
    render inline: '<em>Hello, World!</em>'
  end
end

Here we use inline: instead of plain: to pass the response body string to render, which sets the content type of the response to HTML. Here is what our response will look like:

HTTP/1.1 200 OK
Cache-Control: max-age=0, private, must-revalidate
Connection: Keep-Alive
Content-Length: 22
Content-Type: text/html; charset=utf-8
Date: Tue, 23 Feb 2016 02:44:36 GMT
Etag: W/"1e401bc81108cb3e3dba191111bf9059"
Server: WEBrick/1.3.1 (Ruby/2.3.0/2015-12-25)
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Request-Id: a2eac900-60da-460d-9757-94d8c41c4624
X-Runtime: 0.252767
X-Xss-Protection: 1; mode=block

<em>Hello, World!</em>

Most of the above is very similar to our last HTTP response for the hello world action. Notice, that there are a few differences, and one of those differences is that the response body now has em tags wrapped around it.

Let's inspect our response object within Rails as well:

# assuming we're inside the hello_world action above...

render inline: '<em>Hello, World!</em>'
binding.pry
### inside Pry ###
response.body         # => "<em>Hello, World!</em>"
response.content_type # => "text/html"
response.status       # => 200

Here we see the content type set appropriately to text/html, thanks to using inline: instead of plain:. The status code here also defaults to 200 (OK), which is fine, but if we wanted to change it, we could do so in a render call simply by passing e.g. status: 404 along as well.

Now if you refresh the page, you'll see our Hello, World! message displayed in italics in your browser, thanks to the text/html content type and the <em> tags in the HTML response body string.

Content Using Views

We've got requests being processed by an action now, but there's something amiss: there's content in our action.

The problem here is that a controller's job is not to hold content, it's to… well, control things. A cleanly written controller action won't do much of anything itself, other than orchestrate the calling of other parts of the application and, when needed, put those results together.

A controller action is merely the director of the request handling, it should delegate other responsibilities to other parts of the application. But already, ours is doing something it shouldn't have to: it's holding our content, the HTML message.

To fix this, let's move that HTML out of our action.

Creating a Simple View

HTML content belongs in a View, so let's create one to hold it:

<!-- app/views/application/hello_world.html -->

<html>
  <body>
    <p>Hello, World!</p>
  </body>
</html>

This is the same HTML as before, but now stored in its own view file. And notice where we placed it, because this is important:

app/views/application/hello_world.html

All views live in the app/views directory. From there, they are placed in a directory that corresponds to the controller and action that uses them. Here, this leaves us with an app/views/application/hello_world.html file.

With our view defined, let's make use of it in our controller:

### app/controllers/application_controller.rb ###

class ApplicationController < ActionController::Base
  def hello_world
    render 'application/hello_world'
  end
end

NOTE: Make sure to restart your Rails server with bundle exec rails server before reloading the page

Now, we've used render a couple times already, but here we're doing something different because we're simply passing it a string. Furthermore, you'll note that this string matches the path of the view that we just created. As a result, our hello_world action will now render our HTML response body using our freshly defined application/hello_world view.

Effectively, using render this way is the equivalent of doing:

render inline: File.read('app/views/application/hello_world.html')

Whether we're pointing it to a view or setting the response body in the action itself, keep in mind that it's always the job of render to set the HTTP response body. Though, as we pointed out above, it's typically bad practice to leave content sitting in your controller, so quite commonly you'll see render calls rendering views, like the one above.