I'm working on a Pinterest-like app to learn Rails, where users can collect items and add them to collections.
I have an "Add to collection" button which opens a modal with a list of all collections of a user. This works fine on individual item show pages, but not on my item index page (which lists all items that have been posted).
This is my collections controller:
def index
#item = Item.find(params[:item_id])
#selected_collections = selected_collections(#item.id)
#collections = current_user.collections
respond_to do |format|
format.js {
render
}
end
end
My items controller:
def index
#items = Item.all.order(created_at: :desc).paginate(:page => params[:page], :per_page => 20)
#collections = current_user.collections
#selected_collections = selected_collections(item.id)
end
def show
#item = Item.find_by_id(params[:id])
return render_not_found if #item.blank?
#collections = current_user.collections
#selected_collections = selected_collections(#item.id)
end
This is my Add-to-collection button, which should be shown for each item on the index:
<%= link_to "+ Add", item_collections_path(item, format: :js), class: 'btn btn-secondary btn-30', data: {toggle: 'modal', target: '#CollectionModal'}, remote: true, format: :js %>
And part of the corresponding modal:
<div class="modal-body">
<%= render 'collections/collections_list', :collections=>#collections, :selected_collections=>#selected_collections %>
</div>
_collection_list.html.erb
<div id="collection_list">
<% #collections.each do |collection| %>
<%= render 'collections/collection_checkbox', collection: collection, selected_collections: selected_collections %>
<% end %>
<%= render 'collections/collection_checkbox', collection: Collection.new, selected_collections: selected_collections %>
<div style='display: none;' class="new_collection_template">
<%= render 'collections/collection_checkbox', collection: Collection.new, selected_collections: selected_collections %>
</div>
_collection_checkbox.html.erb
<div class="form-check">
" <%= 'checked' if selected_collections.include?(collection.id) %> <%= "disabled" unless collection.id %>>
<%= collection.collection_name %>
<% if !collection.id %>
<input type="text" class="collection-name" placeholder="New collection" />
<% end %>
On my index I get the following error message:
undefined local variable or method `item' for
ItemsController:0x007f69022aefa0 Did you mean? item_url items_url item_path #items
Screenshot of Error Message
I assume that this is because I don't have individual #item defined on my index. On the item show pages I can simply find the params in the URL. But how can I solve this problem on the index?
Thanks for your help, much appreciated :)
Related
Link to image of what I'm talking about
My problem is that there is an extra template being rendered at the bottom of my list without an item id. The icons delete the item they are next to when clicked. The bottom icon is showing up even though it has no item related to it.
Here is my the show html.erb
<div class="row">
<div class="col-md-8">
<div class="media">
<br />
<div class="media-body">
<h3><%= #user.email %></h3>
<br />
<%= render partial: 'items/form', locals: {user: #user} %>
<div class="media">
<div class="media-body">
<h4 class="media-heading">
<ul>
<%= render #items %>
</ul>
</h4>
</div>
</div>
</div>
</div>
</div>
Here is my _item template
<li><%= item.name %><%= link_to "", [current_user, item], remote: true, method: :delete, class: 'glyphicon glyphicon-ok' %></li>
Here is my items controller
class ItemsController < ApplicationController
def create
#item = Item.new(item_params)
#item.user = current_user
if #item.save
flash[:notice] = "Item was saved."
redirect_to user_path(current_user.id)
else
flash.now[:alert] = "There was an error saving the item. Try again."
redirect_to user_path(current_user.id)
end
end
def destroy
#item = Item.find(params[:id])
if #item.destroy
flash[:notice] = "Item deleted"
redirect_to user_path(current_user.id)
else
flash.now[:alert] = "Error deleting the item."
redirect_to user_path(current_user.id)
end
end
private
def item_params
params.require(:item).permit(:name)
end
end
Here is my users controller
class UsersController < ApplicationController
def show
#user = current_user
#item = Item.new
#items = #user.items
end
end
The items belong to the users and have nested routes under user. The last checkmark (the one I want to get rid of) when hovered over shows the link .../users/6/items, while the others that next to items show .../users/6/items/40
Here is my _form.html.erb for items:
<%= form_for [user, user.items.new] do |f| %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter your to-do item", value: nil, required: true %>
<div class="form-group">
<%= f.submit "Save", class: 'btn btn-success' %>
</div>
<% end %>
Remove
#item = Item.new
from show action in users controller.
class UsersController < ApplicationController
def show
#user = current_user
#items = #user.items
end
end
I've implemented a like/unlike function in my app. On the show page for an item, you can like/unlike without any issues. On a user's profile page though, where it lists all the foods they've uploaded, I have two issues: First, when I click the like button for one food, it triggers it for every food and then the button text under every food says "unlike this food", instead of only having the button text change for just the food that was clicked on. The like gets saved to the db for the correct food, but obviously I don't want the button text to change for foods that I haven't liked. Second, when I try to like a different food on the page without refreshing, the liked gets saved in the db as the original food that I clicked on, instead of the one I actually clicked on.
users.show.html.erb
<% #user.foods.each do |food| %>
<div class="row col-md-12">
<div class="food col-md-4">
<h3 class="title"><%= link_to food.title.capitalize, food_path(food.id) %></h3>
<%= link_to (image_tag (food.image.url)), food_path(food.id) %>
<%= render 'shared/vote_form', :food => food %>
<%= link_to pluralize(food.votes.count, "person"), user_votes_path(:user_id => current_user.id, :food_id => food.id) %> <%= food.votes.count == 1 ? 'wants' : 'want' %> to gobble this
</div>
<div class="description col-md-6">
<% if #user.id == current_user.id %>
<% if food.description.length == 0 %>
<p><%= link_to "Add Description", edit_food_path(food) %></p>
<% else %>
<p><%= food.description %><p>
<p><%= link_to "Edit Description", edit_food_path(food) %></p>
<% end %>
<% if food.recipe.length == 0 %>
<p><%= link_to "Add Recipe", edit_food_path(food) %></p>
<% end %>
<% else %>
<% if food.description.length == 0 %>
<p>No Description</p>
<% else %>
<p><%= food.description %><p>
<% end %>
<% if food.recipe.length == 0 %>
<p>No Recipe</p>
<% else %>
<p><%= link_to "View Recipe", food_path(food) %></p>
<% end %>
<% end %>
</div>
</div>
<% end %>
votes controller
class VotesController < ApplicationController
def index
#votes = Food.find(params[:food_id]).votes
end
def new
#vote = current_user.votes.new
end
def create
#user = current_user
#vote = current_user.votes.build(vote_params)
if #vote.save!
#food = #vote.food
respond_to do |format|
format.html {redirect_to :back, notice: "Liked!"}
format.js
end
puts #vote.food.id
else
puts "No"
redirect_back(fallback_location: root_path)
end
end
def show
#user = current_user
#vote = Vote.find(params[:id])
end
def destroy
#vote = Vote.find(params[:id])
#food = #vote.food
if #vote.destroy!
respond_to do |format|
format.html {redirect_to :back, notice: "Unliked!"}
format.js
end
else
puts "NOOOOOO"
end
end
private
def vote_params
params.require(:vote).permit(:food_id)
end
end
vote partial
<% unless current_user.votes.pluck(:food_id).include?(food.id) %>
<%= button_to "Like This Food", user_votes_path(current_user, { vote: { food_id: food.id } }), :remote => true %>
<% else %>
<% vote = food.votes.where(user_id: current_user.id).first %>
<%= button_to "Unlike This Food", user_vote_path(current_user, vote), :remote => true, method: "delete" %>
<% end %>
create.js.erb
$('.button_to').replaceWith("<%= j (render :partial => 'shared/vote_form', :locals => { :food => #food, :food_id => #food.id }) %>");
destroy.js.erb
$('.button_to').replaceWith("<%= j (render :partial => 'shared/vote_form', :locals => { :food => #food }) %>");
Take a look at your create.js.erb (destroy.js.erb has the same problem). You use select all elements with the class button_to - that is all buttons on the page. Then you call replaceWith that replaces all these buttons with the button provided as the argument. The result is that all buttons get replaced with the like button corresponding to the food you just liked. This is responsible for affecting all buttons on the page and making them refer the food you liked first.
The solution is to add an id attribute to the buttons that would include the ID of the food the button pertains to. For example, if Food with ID 1 is Lettuce then you need to add id="like-1" to the button and change $('.button_to') to $("#like-1").
I know this has been asked before but I think I'm doing everything right and it still wont work.
I have a simple user table one to many with a relationship table. I just want to show the number of people following a selected user in a partial.
It works fine in the non partial view but I keep getting the error:
undefined method `name'
when I make the normal view into a partial and it highlights the focus of the error as being
def gravatar_for(user, size = 30, title = user.name)
which is fromm a helper method I use
the partial is here (called users/_show_follow ):
<% provide(:title, #title) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= gravatar_for user %>
<h1><%= user.name %></h1>
<span><%= link_to "view my profile", user %></span>
</section>
<section class="stats">
<%= render 'shared/stats' %>
<% if users.any? %>
<div class="user_avatars">
<% for user in users %>
<%= link_to gravatar_for(user, size: 30), user %>
<% end %>
</div>
<% end %>
</section>
</aside>
<div class="col-md-8">
<h3><%= #title %></h3>
<% if users.any? %>
<ul class="users follow">
<%= render users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
The originating page for the partial:
<div class="container-fluid">
<div class="row-fluid">
<div class="col-md-8">
<div id="chart"><%= javascript_include_tag('graph') %></div>
</div>
<div class="col-md-2">
<p>
<h2>Node Score</h2>
<div id="FBDiv"></div>
<div id="chart_1"></div>
<h2>Node Profile</h2>
<div id="NodeProfile"></div>
<h2>My inner circle</h2>
<h2>Wants to connect</h2>
<h2>My profile</h2>
<%= current_user.about %>
<section class="stats">
<%= render 'shared/stats' %>
<%= render :partial => "show_follow", :locals => {:user => #user, :users => #users} %>
</section>
</section>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-4"></div>
</div>
</div>
the controller
class UsersController < ApplicationController
def index
#users = User.including_relationships
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
def show
#user = User.find_by name: params[:name]
respond_to do |format|
# Here we need to remove the layout because the request
# is done via ajax and the layout is already loaded.
format.json { render json: #user.to_json }
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
the helper
module ApplicationHelper
def page_header(text)
content_for(:page_header) { text.to_s }
end
def gravatar_for(user, title = user.name, size = 30)
image_tag gravatar_image_url(user.email, size: size), title: title, class: 'img-rounded'
end
end
Is the problem how I pass the variable into the helper method from the partial?
As you pointed out the error is on your helper arguments: def gravatar_for(user, size = 30, title = user.name). user is not an object, it'simply the name of the argument, so user.name won't work.
Try setting a default title (1) or passing the title when you call the method (2):
def gravatar_for(user, size = 30, title = 'Default name')
<%= gravatar_for user, 20, user.name %>. To avoid passing the size each time you can reorder the arguments: def gravatar_for(user, title = user.name, size = 30) or use keyword arguments.
Ive been working to implement a like/unlike feature which works with ajax. I have already built and tested the models and associations which seem to working fine. What I am having problems with is rendering the actual like/unlike button in the view. I have been using this https://github.com/Aufree/ting/tree/master/app/controllers github projects like/unlike system as reference for building my own.
Heres my controller likeships_controller.rb
before_action :authenticate_user!
include LikeshipsHelper
def create
current_user.like!(params[:likeable_type], params[:likeable_id])
respond_to do |format|
format.html { redirect_to correct_path }
format.js
end
end
def destroy
current_user.unlike!(params[:likeable_type], params[:likeable_id])
respond_to do |format|
format.html { redirect_to correct_path }
format.js
end
end
Heres my likeship_helper.rb
module LikeshipsHelper
def likeable_likes_tag like
#count = Likeship.where("likeable_type = ? and likeable_id = ?", like[:likeable_type], like[:likeable_id]).count
if #count > 0
likes_count = #count
else
likes_count = ''
end
if current_user && current_user.liking?(like)
link_title = "unlike"
link_path_method = "delete"
fa_icon = '<i class="red heart icon"></i>'
else
link_title = "like"
link_path_method = "post"
fa_icon = '<i class="red heart empty icon"></i>'
end
if current_user.blank?
"#{likes_count}#{fa_icon}".html_safe
else
link_to "#{likes_count}#{fa_icon}".html_safe,
likeships_path(like),
title: link_title,
class: "animated zoomIn",
method: link_path_method,
remote: true
end
end
def correct_path
if params[:likeable_type].to_s == "Post"
post_path(params[:likeable_id])
end
end
end
these are my views
_like.html.erb
<i class="red heart icon">
<%= form_for(current_user.likeships.build) do |f| %>
<%= f.hidden_field :likeable_type, value: #likeable[:likeable_type] %>
<%= f.hidden_field :likeable_id, value: #likeable[:likeable_id] %>
<%= f.submit %>
<% end %>
</i>
_likes.html.erb
<% if user_signed_in? %>
<% if current_user.liking?(#likeable) %>
<%= render 'likeships/unlike' %>
<% else %>
<%= render 'likeships/like' %>
<% end %>
<% end %>
EDIT: Now if i use this in the _post.html.erb view no error is thrown but no like/unlike button appears either.
<div class="post_wrapper">
<div class="media_wrapper">
<span class="name"><%= link_to post.user.name, post.user, class: "name" %></span>
<p class="media"><%= post.body_html %></p>
<div class="date_and_like">
<span class="item" id="like-post-#{#post.id}" >
<% #likeable = Likeship.likeable(#post) %>
<%= likeable_likes_tag #likeable %>
</span>
<span class="timestamp"><%= time_ago_in_words(post.created_at) %> ago </span>
</div>
</div>
heres the create.js.erb file
$("#like-post-<%= params[:likeable_id] %>").html("<%= j(likeable_likes_tag #likeable) %>")
So I also tried to render the likes form in the post view with this
but it gave me an error that
undefined method `[]' for nil:NilClass
Ive been stumped for hours trying to figure this out any help would be awesome, also let me know if you need any additional info, Thanks.
I want to use two collection #ad_item and #user in my partial. Here is my index erb...
<% if #ad_items.any? && #user.any? %>
<%= render partial: 'yourads/ad_item', collection: {#ad_items,#user} %>
<% end %>
and here is my controller ...
def index
#ad_items = Yourad.all
#user = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #yourads }
end
end
and here is my _ad_item.erb.html partial
<span class="date"><%= ad_item.created_at.to_date() %></span>
<span class="location"><%= ad_item.title %></span>
<div style="float:left"><%= ad_pic #user,ad_item %></div>
<div class="description"><%= ad_item.description %></div>
My helper function is ..
def ad_pic(user,ad)
cl_image_tag("Ad#{user.id}#{ad.id}.jpg", :version => rand(1000000000), :alt => "Ad pic",:width => 70, :height => 70, :crop => :fill)
end
it gives syntax error in ..
<%= render partial: 'yourads/ad_item', collection: {#ad_items,#user} %>
<%= render partial: 'yourads/ad_item', collection: #ad_items ,
locals: {users: #users} %>
and in view
<% for user in users %>
<span class="date"><%= ad_item.created_at.to_date() %></span>
<span class="location"><%= ad_item.title %></span>
<div style="float:left"><%= ad_pic user,ad_item %></div>
<div class="description"><%= ad_item.description %></div>
<% end %>
see Passing Local Variables
<%= render partial: 'yourads/ad_item', :locals => {:ad_items=>#ad_items,:user => #user }%>
you can access #ad_items as ad_items and #user as user in your yourads/ad_item partial