Reputation: 15374
Within my application I can add an object to my CartItem
model but am getting undefined method 'id' for nil:NilClass
, when destroying the record and rendering my view via Ajax.
So upon page load /images/7
for example I have
<div id="cart_form">
<% if @cart_item_collection.include?(@image.id) %>
<%= render 'shared/forms/remove_from_cart', locals: { image: @image } %>
<% else %>
<%= render 'shared/forms/add_to_cart', locals: { image: @image } %>
<% end %>
</div>
class ImagesController < ApplicationController
def show
@image = Image.find(params[:id])
@cart_item_collection = CartItem.where(user_id: current_or_guest_user).pluck(:image_id)
end
end
_add_to_cart.html.erb
<%= form_for(@cart_item, url: cart_item_path, :remote => true do |f| %>
<%= f.hidden_field :image_id, value: @image.id %>
<%= f.submit "Add To Cart" %>
<% end %>
_remove_from_cart.html.erb
<%=button_to cart_item_path(id: @cart_item), method: :delete, :remote => true, class: "icon di_darckblue extra-large button fill", name: 'images' do %>
<i class="di-shopping-cart-up"></i>
Remove From Cart
As previously mentioned I can create a CartItem fine, I render this js.erb file
$("#cart_form").html("<%= j render partial: 'shared/forms/remove_from_cart', locals: { image: @image } %>");
But when I try to destroy the CartItem thats when I get the error
$("#cart_form").html('<%= j render partial: "shared/forms/add_to_cart", locals: { image: @image } %>');
How can I keep @image persisted throughout each call?
Thanks
Upvotes: 1
Views: 418
Reputation: 3358
We need to pass image id when making AJAX calls so that the @image object can be obtained in the views when partials are getting rendered on AJAX completion.
Therefore we fetch the @image
using the image_id parameter.
show.html.erb
<div id="cart_form">
<% if @cart_item_collection.include?(@image.id) %>
<%= render 'shared/forms/remove_from_cart', locals: { image: @image } %>
<% else %>
<%= render 'shared/forms/add_to_cart', locals: { image: @image } %>
<% end %>
<p class="error"></p>
</div>
images_controller.rb
class ImagesController < ApplicationController
def show
@image = Image.find(params[:id])
@cart_item_collection = CartItem.where(user_id: current_or_guest_user).pluck(:image_id)
end
end
_add_to_cart.html.erb
<%= form_for(@cart_item, url: cart_item_path, :remote => true do |f| %>
<%= f.hidden_field :image_id, value: @image.id %>
<%= f.submit "Add To Cart" %>
<% end %>
_remove_from_cart.html.erb
<%=button_to cart_item_path(id: @cart_item, image_id: @image.id), method: :delete, :remote => true, class: "icon di_darckblue extra-large button fill", name: 'images' do %>
<i class="di-shopping-cart-up"></i>
Remove From Cart
Adding to cart,
def add_to_cart
@image = Image.find(params[:cart_item][:image_id])
#if @image is present
#your code
#else
#@error= "Item could not be added to cart."
#end
end
create.js.erb
<% if !@error %>
$("#cart_form").html("<%= j render partial: 'shared/forms/remove_from_cart', locals: { image: @image } %>");
$('.error').html('');
<% else %>
$('.error').html(<%= @error %>);
<% end %>
Removing from cart,
def destroy
@image = Image.find(params[:image_id])
#if @image
#destroying @cart_item
#rendering destroy.js.erb
#@cart_item = CartItem.new
#else
@error="Item could not be removed from cart"
#end
end
destroy.js.erb:
<% if !@error %>
$("#cart_form").html('<%= j render partial: "shared/forms/add_to_cart", locals: { image: @image } %>');
$('.error').html('');
<% else %>
$('.error').html(<%= @error %>);
<% end %>
Another way: As you are just using image id inside the partials: here we are just passing the image_id in controller and sending it back to front end.
But the only check that we would have to do is checking if the image exists for the image_id passed to controller, as there are chances that user can change the value in front end.
To check that we would have to check for presence of image on every AJAX call made, which makes the first approach more safer. But if that doesn't matter to you then you can go with this approach.
<div id="cart_form">
<% if @cart_item_collection.include?(@image.id) %>
<%= render 'shared/forms/remove_from_cart', locals: { image_id: @image.id } %>
<% else %>
<%= render 'shared/forms/add_to_cart', locals: { image_id: @image.id } %>
<% end %>
</div>
class ImagesController < ApplicationController
def show
@image = Image.find(params[:id])
@cart_item_collection = CartItem.where(user_id: current_or_guest_user).pluck(:image_id)
end
end
_add_to_cart.html.erb
<%= form_for(@cart_item, url: cart_item_path, :remote => true do |f| %>
<%= f.hidden_field :image_id, value: image_id %>
<%= f.submit "Add To Cart" %>
<% end %>
_remove_from_cart.html.erb
<%=button_to cart_item_path(id: @cart_item, image_id: image_id), method: :delete, :remote => true, class: "icon di_darckblue extra-large button fill", name: 'images' do %>
<i class="di-shopping-cart-up"></i>
Remove From Cart
Adding to cart, I render this js.erb file,
def add_to_cart
@image_id = params[:cart_item][:image_id]
#your code
end
create.js.erb
$("#cart_form").html("<%= j render partial: 'shared/forms/remove_from_cart', locals: { image_id: @image_id } %>");
Removing from cart,
def destroy
@image = params[:image_id]
#destroying @cart_item
#rendering destroy.js.erb
end
destroy.js.erb:
$("#cart_form").html('<%= j render partial: "shared/forms/add_to_cart", locals: { image_id: @image_id } %>');
Upvotes: 2