Displaying articles on Ruby on Rails website

We still need a way to display a list of all our articles, let’s make one. The route displayed with bin/rails routes is the following:

articles GET /articles(.:format) articles#index

Add an index action corresponding to this route inside the ArticlesController in the app/controllers/articles_controller.rb file. When we write an index action, it’s common practice to place it as the first method in a controller. Let’s do it:

class ArticlesController < ApplicationController

  def index

    @articles = Article.all

  end

  def show

    @article = Article.find(params[:id])

  end

  def new

  end

  # omitted for brevity

And finally, the view for this action, located at app/views/articles/index.html.erb:

<h1>Listing articles</h1>

<table>

  <tr>

    <th>Title</th>

    <th>Text</th>

    <th></th>

  </tr>

  <% @articles.each do |article| %>

    <tr>

      <td><%= article.title %></td>

      <td><%= article.text %></td>

      <td><%= link_to ‘Show’, article_path(article) %></td>

    </tr>

  <% end %>

</table>

Now if you go to http://localhost:3000/articles you can see a list of all the articles you have already created.

Adding a link

You can now create and view individual and all articles. Let’s add some links to navigate between pages.

Open app/views/welcome/index.html.erb and change it like this:

<h1>Hello Rails!</h1>

<%= link_to ‘My Blog’, controller: ‘articles’ %>

The link_to method is one of Rails’ built-in helpers. It creates a hyperlink based on the text to display and indicate where to go – in our case the path for the articles controller.

Let’s add links to other views as well, starting by adding a “New Article” link to app/views/articles/index.html.erb, placing it above the <table> tag:

<%= link_to ‘New article’, new_article_path %>

This link will take you to the form to create a new article.

Now add another link in app/views/articles/new.html.erb, below the form, to get back to the index action:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

  …

<% end %>

<%= link_to ‘Back’, articles_path %>

Finally, add a link to the app/views/articles/show.html.erb template to also return to the index action so that viewers of the individual article can go back and view the full list again:

TIP: If you want to refer to an action on the same controller, you don’t need to specify the :controller option because Rails uses the current controller by default.

TIP: In development mode (which you use by default), Rails reloads your application with every browser request, so you don’t have to stop and restart your web server when you make changes.

Let’s add some validations

The model file app/models/article.rb is as simple as it looks:

class Article < ApplicationRecord

end

Not much is written in this file, but note that the Article class is derived from ApplicationRecord. ApplicationRecord inherits from ActiveRecord::Base, which provides a wealth of functionality for your Rails models, including basic database operations CRUD (Create, Read, Update, Destroy), data validation, sophisticated search support, and the ability to establish relationships between different models.

Rails includes methods to help validate the data you pass into the model. Open the app/models/article.rb file and edit:

class Article < ApplicationRecord

  validates :title, presence: true,

                    length: { minimum: 5 }

end

These changes will ensure that all articles have a title that is at least five characters long. Rails can check for various conditions in the model, including the existence or uniqueness of fields, their format, and the existence of related objects. More validations are covered in Active Record Validation.

Now that there are validations, calling @article.save on an invalid article will return false. If you open app/controllers/articles_controller.rb again, you’ll see that we don’t check the result of calling @article.save in the create action. If @article.save fails in this situation, we need to show the form to the user again. To do this, replace the new and create actions in app/controllers/articles_controller.rb with these:

def new

  @article = article.new

end

def create

  @article = Article.new(article_params)

  if @article.save

    redirect_to @article

  else

    render ‘new’

  end

end

private

  def article_params

    params.require(:article).permit(:title, :text)

  end

Now the new action creates a new instance variable called @article, and you’ll see why in a couple of paragraphs.

Note that in the create action we used render instead of redirect_to when save returns false. The render method is used to have the @article object passed back to the new template when it is rendered. This rendering is done within the same request as the form submission, while redirect_to tells the browser to make another request.

If you reload http://localhost:3000/articles/new and try to save an article without a title, Rails will take you back to the form, but that’s not very helpful. You need to tell the user that something went wrong. To do this, modify app/views/articles/new.html.erb to check for error messages:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

  <% if @article.errors.any? %>

    <div id=”error_explanation”>

      <h2>

        <%= pluralize(@article.errors.count, “error”) %> prohibited

        this article from being saved:

      </h2>

      <ul>

        <% @article.errors.full_messages.each do |msg| %>

          <li><%= msg %></li>

        <% end %>

      </ul>

    </div>

  <% end %>

  <p>

    <%= form.label :title %><br>

    <%= form.text_field :title %>

  </p>

  <p>

    <%= form.label :text %><br>

    <%= form.text_area :text %>

  </p>

  <p>

    <%= form.submit %>

  </p>

<% end %>

<%= link_to ‘Back’, articles_path %>

A few things about what’s going on. We check if there are any errors with @article.errors.any?, in which case we show a list of all errors with @article.errors.full_messages.

pluralize is a rails helper that takes a number and a string as arguments. If the number is greater than one, the string will be automatically pluralized.

The reason we added @article = Article.new to the ArticlesController is because otherwise @article would be nil in the view, and calling @article.errors.any? will cause an error.

TIP: Rails automatically wraps errored fields in a div with class field_with_errors. You can define a css rule to make them stand out.

We will now have a nice error message when saving an article without a title if you try this in the form of a new article

Leave a Reply

Your email address will not be published.