Skip to content
Dave Strus edited this page Jul 16, 2015 · 1 revision

Now that we have relationships between posts and categories, seems like we ought to be able to browse posts by categories. Let's make categories#show do just that.

What will we need to do in the controller's show method? As usual for a show action, we'll grab the appropriate instance of Category.

app/controllers/categories_controller.rb

  def show
    @category = Category.find params[:id]
  end

In our view, we'll want to iterate over each post in that category. If we call @category.posts, it will load the posts from the database at that time. Since we know we'll always need the posts in this view, we can load them in the controller in the very same query as the category itself using includes.

  def show
    @category = Category.includes(:posts).find(params[:id])
  end

Moving on to the view, let's start simple. We'll display the title and description of our category.

New file: app/views/categories/show.html.erb

<div class="category">
  <h2><%= @category.title %></h2>
  <div class="description"><%= @category.description %></div>
</div>

Remember the sidebar attribute we added? How can we display the value of that attribute in the sidebar? The thing is, the sidebar is in the layout, not in the view. Just as we yield the output of our view in the layout, we can yield other data to the layout, using the content_for method from ActionView::Helpers::CaptureHelper.

Calling #content_for stores a block of markup in an identifier for later use. In order to access this stored content in other templates, helper modules or the layout, you would pass the identifier as an argument to content_for.

Note: yield can still be used to retrieve the stored content, but calling yield doesn't work in helper modules, while content_for does.

Let's define a helper method to store data in a :sidebar identifer.

app/helpers/application_helper.rb

  def sidebar(sidebar_content)
    content_for(:sidebar) { sidebar_content }
  end

We can then call that method from our view to store some data. We'll add it at the very beginning of the view.

app/views/categories/show.html.erb

<% sidebar(@category.sidebar) -%>

Now the :sidebar identifier will be storing the value of @category.sidebar. We can use yield in our layout to retrieve that data and display it under the links in our sidebar.

app/layouts/application.html.erb

        <nav id="sidebar">
          <%= link_to 'Submit a new link', new_post_path %>
          <%= link_to 'Submit a new text post', new_post_path(post_type: :text) %>
          <%= link_to 'Create a new category', new_category_path %>
          <div><%= yield :sidebar %></div>
        </nav>

Let's add a tiny bit of CSS:

app/assets/stylesheets/style.scss

.category {
  h2 {
    margin-top: 0;
  }
  .description {
    padding-bottom: 0.5em;
    border-bottom: 1px solid gray;
    margin-bottom: 0.5em;
  }
}

If you can see the title, description, and sidebar for categories, go ahead an make an incremental commit. I'm going to use the abbreviation "WIP" to indicate that this commit contains a work-in-progress.

$ git add .
$ git commit -m "WIP display category data on `categories/show`."

There's something very obviously missing from this page still: Posts. We really want them to display just as they do on posts/index. Are you thinking what I'm thinking? It's partial time.

LAB

Extract post display from posts/index into a partial, and render the partial in categories/show to display only the posts in a particular category.