Search code examples
ruby-on-railsrubyruby-on-rails-4partialsrenderpartial

Rails partial template with collection not rendering correctly


I have been troubleshooting this error for hours and I have no idea what I’m doing wrong. I simply can’t get the items collection to properly display for an individual user. Each item belongs to a single user and each user has many items. Right now, my partial is not rendering on the users show view.

If I change <%= render partial: "/users/item", collection: @user.items %> to <%= render partial: "/users/item", collection: @items %> then the partial template is rendered, but all items are displayed regardless of which user they belong to. After checking the database, each item entry is being properly stored with a foreign key user_id, so that isn’t the issue.

How do I get the correct items to display for each user? I have used partials several times before and I’ve cross-referenced old and new code. I can’t see the difference and I’m losing my mind. I’ve searched the general internets, Stack Overflow, and reviewed the Rails Guides for Layouts and Rendering. I've posted the relevant code below and happy to post anything else. Please help!

views/users/_item.html.erb

<div>
  <h4>ITEM: <%= item.title %></h4>
  <p class="item"><%= item.cost %></p>
</div>

views/users/show.html.erb

<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>

<div><%= @user.first_name %></div>

<h3>My List</h3>
<%= render partial: "/users/item", collection: @user.items %>

<span>ADD ITEM&nbsp;<%= link_to image_tag("Plus-50.png"), new_user_item_path(@user), class: "items-nved" %> </span>

controllers/users_controller.rb

class UsersController < ApplicationController

  before_action :set_user, only: [:show, :edit, :update, :destroy]

  def index
    @users = User.all
    render :index
  end

  def show
    @user = User.find(params[:id])
    # @item = Item.find(params[:id])
    @items = Item.all
    @item = Item.new
    @item.user_id = @user.id
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to new_session_path, method: :get
    else
      render "new"
   end
  end

  def edit
  end

  def update
  end

  def destroy
  end

  private
  def set_user
   @user = User.find(params[:id])
  end

  def user_params
    params.require(:user).permit(:first_name, :last_name, :birthday, :city, :state, :email, :password, :password_confirmation)
  end
end

controllers/items_controller.rb

class ItemsController < ApplicationController

  include ItemsHelper

  before_filter :require_login, only: [:new, :create, :update, :destroy]

  def index
    @current_user = current_user
    @items = Item.all
  end

  def show
    @user = current_user
    @item = Item.find(params[:id])
  end

  def new
    @user = current_user
    @item = Item.new
  end

  def create
    @user = current_user
    @item = Item.new(item_params)
    @item.user_id = params[:user_id] # set user_id param from database to current_user id
    @item.save

    redirect_to user_item_path(@user, @item)
  end

  def edit
    @user = current_user
    @item = Item.find(params[:id])
  end

  def update
   @user = current_user
   @item = Item.find(params[:id])
   @item.update(item_params)
   redirect_to user_path(@user)
  end

  def destroy
    @user = User.find params[:user_id]
    @item = Item.find(params[:id])
    @item.destroy
    redirect_to user_path(@user)
  end
end

private
  def user_params
    params.require(:user).permit(:first_name, :last_name, :birthday, :city, :state, :email, :password, :password_confirmation)
  end

  def require_login
    unless logged_in?
      flash[:error] = "You must be logged in to access this section"
      redirect_to new_login_url # halts request cycle
    end
  end

Solution

  • Install the byebug gem, either by running gem install byebug on your command line or by putting byebug in your Gemfile (then run bundle install).

    Put byebug just after the @user declaration in your UsersController, like this:

    class UsersController < ApplicationController
    
      before_action :set_user, only: [:show, :edit, :update, :destroy]
    
      def index
        @users = User.all
        render :index
      end
    
      def show
        @user = User.find(params[:id])
        byebug
    

    Then start your local server and go the user show view in your browser. The page won't render, because when it hits byebug the server pauses.

    When this happens, go back to your Terminal and look at your server log. It will be waiting for your commands. Type @user and press Enter. What does it return?

    Also try @user.inspect. Does it return a User object?

    Then do @user.items. What does that return? If @user.items returns the expected collection of items, then there is something wrong with how you are passing that to the view, or what you're doing with it in the view.

    If @user.items doesn't return a collection of items, then that's why it isn't showing in your view! In this case, it's probably something to do with your ActiveRecord associations and/or your migrations.

    Another possibility is that the current Items in your database may have been created before you had the associations setup, so they won't have a user_id. You can check this in your rails console. Make sure you have Items in your database that have the same user_id as your current_user.

    Another good thing to do in your rails console would be to assign a User, that you think has some Items, to a variable eg user = User.first, and then do user.items. Does that work?

    I also recommend writing some RSpec tests, at least at Model level to test that the proper associations exist. The shoulda gem is great for this.

    Comment here with your findings.