Fork me on GitHub
Learn more at the sources:

Rails Views and Controller

Rails Views and Controller

The Rails View is concerned with displaying (HTML and JSON) output. The controller is concerned with handling incoming requests, and using the model and views to generate a result.

After reading this guide you should

  • understand the role of view, controller and routing in rails
  • know which routes and actions are implied by rails resources

and be able to

  • adapt the views and controllers generated by the scaffold generator
  • build a form for editing a model
  • use nested resources

Fork the example app 'kanban-board' and try out what you learn here.

1 The View

The View in the Model-View-Controller pattern is responsible for generating output that will be displayed to the user in various ways.

In Ruby on Rails Embedded Ruby (ERB) is normally used as the templating languages to define the views. This will be familiar to you if you have used templates in other languages or have used PHP embedded in HTML.

Both Web Designers and Web Developers might want to edit the files concerning the view. (This is one of the points where using git to resolve merge conflicts comes in handy!).

1.1 Layouts and Views

The view file (e.g. app/views/thing/show.html.erb will only contain the HTML that is specific for this view. There is another file app/views/layouts/application.html.erb that contains the surrounding stuff that stays the same for every page in the app: head, main navigation, footer.

Layouts and Views

If you find other parts of your code that you want to reuse in several views you can extract it into a "partial". An example is the _form.html.erb partial created by the scaffold: it is used both by the new.html.erb and the edit.html.erb view.

Layouts, Views and Partials

1.2 Views with ERB

When it comes to templating systems there are two competing schools of thought: on the one side there are minimal "logic-less" templating systems that only offer the inclusion of variable values and maybe iteration. On the other hand are full programming languages embedded in HTML.

ERB is an example of the latter: the full power of Ruby is available inside the template:

  • Instance Variables of the controller are available in the view
  • <% ruby code here %> just evaluates the code
  • <%= ruby code here %> evaluates the code and includes the result

You can use all the usual ruby contructs for iteration, conditions, blocks. Here's an example of a loop:

<ul>
<% @groups.each do |group| %>
  <li><%= link_to group.name, group %></li>
<% end %>
</ul>

Never write links to your own app "by hand"! Use helper methods to get the right URLs. Use rails routes on the command line do find out which URLs exist:

$ rails routes
          Prefix Verb     URI Pattern
  add_user_group PUT      /groups/:id/add_user(.:format)
  del_user_group PUT      /groups/:id/del_user(.:format)
          groups GET      /groups(.:format)
                 POST     /groups(.:format)
       new_group GET      /groups/new(.:format)
      edit_group GET      /groups/:id/edit(.:format)
           group GET      /groups/:id(.:format)
                 PATCH    /groups/:id(.:format)
                 PUT      /groups/:id(.:format)
                 DELETE   /groups/:id(.:format)

Use the "prefix" from rails routes and _path or _url to get the path or full URL of the action.

  • link_to "Add a User", add_user_group_path links to the groups#add_user action
  • link_to "Show the Object", object links to the show action of the object

Now do 'Rails for Zombies' Episode no3

Rails for Zombies Episode 3

2 The Controller

The controller is the central part of MVC. An incoming HTTP request is routed to exactly one controller action that will respond to the request. The controller then uses the model(s) to load and manipulate the right data, and finally displays the resulting page by rendering a view.

2.1 Restful Resources

Rails uses REST as a convention for which actions should be available. For example if you specify in config/routes.rb

resources :zombies

This will generate the following mappings (visibile through rails routes):

HTTP
Method URI Pattern       Controller          Action
GET    /zombies          zombies_controller  def index
POST   /zombies          zombies_controller  def create
GET    /zombies/new      zombies_controller  def new
GET    /zombies/:id/edit zombies_controller  def edit
GET    /zombies/:id      zombies_controller  def show
PATCH  /zombies/:id      zombies_controller  def update
DELETE /zombies/:id      zombies_controller  def destroy

You have already used the scaffold generator which also adds the necessary views. This way we end up with full CRUD (create, read, update, delete) capability for zombies:

scaffold

Do adapt the views to better fit your users needs, but try to keep the underlying routes the same!

2.2 Now do 'Rails for Zombies' Episode no 4

Rails for Zombies Episode 4

3 Form Helpers

When you write a Rails App, you never write form- or input-Tags by hand. You always use form helpers to contruct the HTML for you. You gain a lot of functionality by using the helpers, but you also need to understand how they work.

3.1 Creating the Form Tag

Let's look at a very simple edit-form for a Resource called user:

<%= form_for(@user) do |f| %>
    Uid:      <%= f.text_field :uid %>  <br>
    Name:     <%= f.text_field :name %> <br>
    E-Mail:   <%= f.email_field :email %> <br>
    Homepage: <%= f.url_field :homepage %> <br>
    <%= f.submit %>
<% end %>

The form helper form_for will create the form-tag and set the action and method according to the REST conventions. For example if the @user variable contains a user object with id 16, the resulting form tag will look like this:

<form class="edit_user" id="edit_user_16" action="/users/16" accept-charset="UTF-8" method="post">
  <input type="hidden" name="_method" value="patch" />
...
</form>

The REST conventions say we should use the PATCH method for updating an existing resource, but this is not available in html forms currently. Current browsers only support Html forms using GET or POST methods. Rails gets around this restriction by using a hidden field and some javascript to actually send the HTTP request with the correct method.

3.2 Creating Input Elements

<%= form_for(@user) do |f| %>
    Uid:      <%= f.text_field :uid %>  <br>
    Name:     <%= f.text_field :name %> <br>
    E-Mail:   <%= f.email_field :email %> <br>
    Homepage: <%= f.url_field :homepage %> <br>
    <%= f.submit %>
<% end %>

The text_field, email_field, url_field helpers create input fields that are related to the attributes of the user-object. The fields are set up correctly for both displaying the current value of the attribute and for editing and overwriting it when the form is sent in. For example <%= f.text_field :name %> might be displayed as

<input type="text" value="Brigitte Jellinek" name="user[name]" id="user_name" />

3.3 Sending the Data

When you press the submit-button, the data from the form is sent via a HTTP request. In the development-log file on the server you can see the request coming in and being routed to the right action:

Started PATCH "/users/16" for ::1 at 2015-11-11 12:47:15 +0100
Processing by UsersController#update as HTML
  Parameters: { "user"=>{"uid"=>"4206851", "name"=>"Brigitte Jellinek", "email"=>"", ... }, "id"=>"16"}

Actually the Parameter came in through two separate channels: the data for the user came through the body of the HTTP request, the id came in as part of the URL. Observe the URI Pattern in the output of rails routes:

          Prefix Verb     URI Pattern                        Controller#Action
           users GET      /users(.:format)                   users#index
                 POST     /users(.:format)                   users#create
        new_user GET      /users/new(.:format)               users#new
       edit_user GET      /users/:id/edit(.:format)          users#edit
            user GET      /users/:id(.:format)               users#show
                 PATCH    /users/:id(.:format)               users#update

Because the pattern is /users/:id a request to /users/16 will also set the id to 16.

3.4 Processing the Data

The parameters from the HTTP request can be used directly in the controller via the params Hash:

# PATCH /users/1
# PATCH /users/1.json
def update
  @user = User.find(params[:id])
  ...
end

For mass-assignments (update, create) rails offers an easy way to filter out only the parameters you really want to be changed / created, and ignore all others. The following code will look for a key user in the params Hash and only pick out its sub-entries uid, name, email and homepage:

# PATCH /users/1
# PATCH /users/1.json
def update
  @user = User.find(params[:id])
  good_params = params.require(:user).permit(:uid, :name, :email, :homepage)
  @user.update(good_params)
  redirect_to @user, notice: 'User updated.'
end

We should also handle errors. If the update does not succeed, update returns false and the errors attribute is set on the user-object. In this case we just re-display the edit-view from before:

# PATCH /users/1
# PATCH /users/1.json
def update
  @user = User.find(params[:id])
  good_params = params.require(:user).permit(:uid, :name, :email, :homepage)
  if @user.update(good_params)
    redirect_to @user, notice: 'User was successfully updated.'
  else
    render :edit 
  end
end

3.5 Handling and Displaying Errors

When displaying the form we always display errors that are available throught the errors attribute of the user object:

<%= form_for(@user) do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  ...
<% end %>

4 Further reading