Reputation: 151
I need to display products by chosen categories using Ajax; if the user didn't choose a category, then all the products are displayed. If the user chose one or two categories, then only products that belong to these categories should be displayed.
Example:
My models Product, Category, and join table causes_products
Category
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
end
Products
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
end
I was looking for a tutorial and I found "Rails 4: "How to partials & AJAX, dead easy". I followed this tutorial but I got an error. I feel like I missed something.
Can you please advise me on this or refer to a helpful source. Thank you!
products controller:
class ProductsController < ApplicationController
def index
@products = Product.all
@categories = Category.all
end
def from_category
@selected = Product.where(:category_id => params[:cat_id])
respond_to do |format|
format.js
end
end
end
products/index.html.erb:
<div class="grid">
<%= render 'sidebar_menu' %>
<%= render partial: 'product_grid', locals: { products: @products} %>
</div>
products/_sidebar_menu
<% @categories.each do | cat | %>
<%= link_to cat.name, fetch_products_path(:cat_id => cat.id), :remote => true %>
<% end %>
routes.rb
get "/fetch_products" => 'products#from_category', as: 'fetch_products'
products/_products_grid.html.erb
<div>
<%= render partial: 'products_list', locals: { products: products } %>
</div>
products/_products_list.html.erb
<% products.each do |product| %>
<div class="product_box">
product.name
</div>
<% end %>
products/from_category.js.erb
$("#products_grid").html("<%= escape_javascript(render partial: 'products_list', locals: { products: @selected } ) %>");
my terminal output:
Product Load (3.2ms) SELECT `products`.* FROM `products` WHERE `products`.`category_id` = '11'
Mysql2::Error: Unknown column 'products.category_id' in 'where clause': SELECT `products`.* FROM `products` WHERE `products`.`category_id` = '11'
Rendered products/_products_list.html.erb (8.8ms)
Rendered products/from_category.js.erb (12.2ms)
Completed 500 Internal Server Error in 23ms (ActiveRecord: 3.5ms)
ActionView::Template::Error (Mysql2::Error: Unknown column 'products.category_id' in 'where clause': SELECT `products`.* FROM `products` WHERE `products`.`category_id` = '11'):
1: <% products.each do |product| %>
2: <div class="product_box">
3: <%= product.name %>
4: </div>
app/views/products/_products_list.html.erb:1:in `_app_views_products__products_list_html_erb___255698018512654633_70336949043820'
app/views/products/from_category.js.erb:1:in `_app_views_products_from_category_js_erb___4172137050793767106_70336965664480'
app/controllers/products_controller.rb:24:in `from_category'`
* - originally sidebar_menu file contained only one line:
<%= link_to cat.name, fetch_items_path(:cat_id => cat.id), :remote => true %>
but I got this error:
Showing views/products/_sidebar_menu.html.erb where line #1 raised:
undefined local variable or method `cat' for #<#<Class:0x007ff1336747c0>:0x007ff1350376a8>
Extracted source (around line #1):
<%= link_to cat.name, fetch_products_path(:cat_id => cat.id), :remote
=> true %>
@max's solution:
def from_category
@products = Product.joins(:categories).where(categories: { id: params[:cat_id] })
respond_to do |format|
format.js
end
Thanks to @max for his help, his solution was to update controller action, it solved the error but the list of products isn't being refreshed after selecting a category.
log:
Started GET "/fetch_products?cat_id=11" for ::1 at 2016-04-18 13:53:01 +0300
Processing by ProductsController#from_category as JS
Parameters: {"cat_id"=>"11"}
Product Load (1.9ms) SELECT `products`.* FROM `products` INNER JOIN `categories_products` ON `categories_products`.`product_id` = `products`.`id` INNER JOIN `categories` ON `categories`.`id` = `categories_products`.`category_id` WHERE `categories`.`id` = 11
Rendered products/_products_list.html.erb (6.4ms)
Rendered products/from_category.js.erb (11.5ms)
Completed 200 OK in 29ms (Views: 24.1ms | ActiveRecord: 1.9ms)
@max That's what I get in the response when I click on a category:
$("#products_grid").html("
<div class=\"product_box\">\n Bed\n <\/div>\n
<div class=\"product_box\">\n Notebook\n <\/div>\n
<div class=\"product_box\">\n Desk\n <\/div>\n
<div class=\"product_box\">\n Mug\n <\/div>\n"
);
@Jeffrey M Castro noticed that I was missing the products_grid
id in the div
:
products/_products_grid.html.erb
<div id="products_grid">
<%= render partial: 'products_list', locals: { products: products } %>
</div>
The code works great only for one chosen category, but I would like it to be able to filter two or more categories as well. What changes do I need to make?
Scenario: User clicks the 'Home category', he gets all the products that belong to this category. Then he clicks on the 'Shoes category' and now he sees all the products that belong to both categories ('Home category' or 'Shoes category'). User clicks on the 'Clear All' button (in order to reset the chosen categories), now all the products from all the categories are displayed.
Thanks.
Upvotes: 0
Views: 658
Reputation: 377
Following responses from @max, it is a good practice to check the JS Response that your server gives using the network tab of your browsers inspector.
In your code:
$("#products_grid").html("<%= escape_javascript(render partial: 'products_list', locals: { products: @selected } ) %>");
You want to change the html content of #products_grid
, but I do not see in your view files anything that has that ID.
So you have to specify in the div that its id is #products_grid
:
products/_products_grid.html.erb
<div id="products_grid">
<%= render partial: 'products_list', locals: { products: products } %>
</div>
Upvotes: 2
Reputation: 102036
Since you have a has_and_belongs_to_many
relation between products and categories you cannot select products by products.category_id
since the actual link between the two is stored in the categories_products
join table.
You need to use a join and specify the condition on the association instead:
@products = Product.joins(:categories)
.where(categories: { id: params[:cat_id] })
Upvotes: 2