I have been battling with what is going to turn out to be a quick answer for someone...
I have view with nested partials. The index has a partial to the categories which has a partial to the subcategories.
The categories are looping as expected but for some reason the locals don't seem to be reaching the controller.
My code is as follows:
index.html.erb
<%= render partial: "categories/categories", object: #categories %>
_categories.html.erb
<% #categories.each do |category| %>
<% if category.category_parent == 0 %>
<li><%= category.category_name %> <i class="icons icon-right-dir"></i>
<%= render partial: "categories/subcategories", object: #subcategories, locals: {parent_id: category.category_id} %>
</li>
<% end %>
<% end %>
_subcategories.html.erb
<% if #parent != nil %>
<ul class="sidebar-dropdown">
<li>
<ul>
<% #subcategories.each do |subcategory| %>
<li><%= subcategory.category_name %> <i class="icons icon-right-dir"></i></li>
<% end %>
</ul>
</li>
</ul>
<% end %>
categories_controller.rb
class CategoriesController < ApplicationController
before_action :categories, :subcategories
def categories
#categories = Category.all
end
def subcategories
#parent = params[:parent_id]
#subcategories = Category.where(:category_parent => params[:parent_id])
end
end
#categories is an instance variables, and you shouldn't need to pass it from view to view. So the first render statment becomes:
<%= render "categories/categories" %>
(I'm more familiar leaving off partial:)
_categories.html.erb becomes this:
<% #categories.each do |category| %>
<% if category.category_parent == 0 %>
<li><%= category.category_name %> <i class="icons icon-right-dir"></i>
<%= render "categories/subcategories", category: category %>
<%# Note passing in category to have access to it as a local %>
</li>
<% end %>
<% end %>
_subcategories.html.erb
<% if category != nil %>
<ul class="sidebar-dropdown">
<li>
<ul>
<% category.subcategories.each do |subcategory| %>
<li><%= subcategory.category_name %> <i class="icons icon-right-dir"></i></li>
<% end %>
</ul>
</li>
</ul>
<% end %>
I don't think you should be looking up #subcategories in your controller. It looks like in def categories you're just getting all the categories and then looping through them in the view. As it is, this will be n+1, as every time it loops over a new category, it will have to do a db query to find its subcategories. You can avoid this with includes.
categories_contorller.rb
def categories
#categories = Category.all.includes(:subcategories)
end
This is what I think is simplest and clearest. But I also haven't played with object: too much. This post might help you if you want to stick to that pattern:
Related
In my index.html.erb file, this lot of code works:
<% if Restaurant.all.any? %>
<% Restaurant.all.each do |restaurant| %>
<h2><%= restaurant.name %></h2>
<% end %>
<% else %>
<h1>No restaurants yet!</h1>
<% end %>
<a href='#'>Add a restaurant</a>
However, when I change the controller to this:
class RestaurantsController < ApplicationController
def index
#restaurants = Restaurant.all
end
end
and the index.html.erb file to this:
<% if #restaurants.any? %>
<% #restaurants.each do |restaurant| %>
<h2><%= restaurant.name %></h2>
<% end %>
<% else %>
<h1>No restaurants yet!</h1>
<% end %>
<a href='#'>Add a restaurant</a>
all of the tests which passed, now fail because of that change, and I can't for the life of me work out why they are not working. The error message when running the tests is:
Failure/Error: visit '/restaurants'
ActionView::Template::Error:
undefined method `any?' for nil:NilClass
My routes.rb file is as follows:
Rails.application.routes.draw do
resources :restaurants
end
Actually the problem is that your index is called directly without going to controller. So earlier your "Restaurant.all" works correctly but now atfer new changes #restuanants is null and you applied null.any?. It gives error.
try
<% if #restaurants && #restaurants.any? %>
or
<% unless #restaurants.blank? %>
Try this I hope this will help
In index.html.erb
<% unless #restaurants.blank? %>
<% #restaurants.each do |restaurant| %>
<h2><%= restaurant.name %></h2>
<% end %>
<% else %>
<h1>No restaurants yet!</h1>
<% end %>
<a href='#'>Add a restaurant</a>
I will give an example.
Say I have Category and Product as my two models and another ChosenProduct that specifies/handles the many to many relationship between them. Now when I have certain categories and I am iterating in a loop on these categories getting the corresponding products. How to access these multiple product objects in my view?
I am new to Rails and MVC so for now I am doing this task in my view itself. But I think this shouldn't be the right way. How should I approach this ?
I am adding my view code as asked. This works for me fine now. But I don't think its pretty.
<ul>
<% #categories.each do |category| %>
<li>
<%= image_submit_tag("add.png", :height => "20", :width => '20', :name=>"add_product_to_#{category.id}", :id=>"add_product_to_#{category.id}" ) %>
<%= category.category_name %>
<% #chosen_products = category.chosen_products %>
<% #chosen_products.each do |chosen_product| %>
<% #products = Product.where(:id => chosen_product.product_id).all %>
<% #products.each do |product| %>
<ul>
<li><%= product.product_name %>
<ul>
<li><%= image_tag("#{product.product_image_url}", :size => "200x200", :alt => "Can not load image!") %></li>
<li><%= product.product_image_url %></li>
</ul>
</li>
</ul>
<% end %>
<% end %>
<% end %>
</li>
</ul>
To iterate over a collection of objects of the same class, you must use a partial, it will iterate over your collection for you. Suppose this:
#controller
def index
#categories = Category.all
end
Then you will need a partial view called _category.html.erb in your views/categories/ with the logic of your view. Lets suppose your partial is:
<div class="info">
<p class="name"><%= category.name %></p>
</div>
The partial will receive a collection of categories, and it will iterate over the collection automatically. So in the partial you must call the object in the singular form, in this case category to refer the actual object that is being rendered.
Then in your index.html.erb view, all you need is to render the partial.
<%= render #categories %>
or
<%= render(partial: "categories/category", collection: #categories) || "<h1>There are no categories</h1>".html_safe %>
The second option will render what is next to || in case the collection #categories is empty.
I assume you have defined the associations between models as
app/models/chosen_product.rb
class ChosenProduct < ActiveRecord::Base
belongs_to :category
belongs_to :product
end
app/models/category.rb
class Category < ActiveRecord::Base
has_many :chosen_products
has_many :products, through: :chosen_products
end
So in your your view you could do
<ul>
<% #categories.each do |category| %>
<li>
<%= image_submit_tag("add.png", :height => "20", :width => '20', :name=>"add_product_to_#{category.id}", :id=>"add_product_to_#{category.id}" ) %>
<%= category.category_name %>
<% category.products.each do |product| %>
<ul>
<li><%= product.product_name %>
<ul>
<li><%= image_tag("#{product.product_image_url}", :size => "200x200", :alt => "Can not load image!") %></li>
<li><%= product.product_image_url %></li>
</ul>
</li>
</ul>
<% end %>
</li>
<% end %>
I was trying to display a list of the blog with the same category which display on the url parameter. however when i click the link it still stay at the same page.
route
match '/microposts/:category', :to => 'microposts#category_list'
view
<h2>sidebar</h2>
<% #categories= Micropost.select("category").group("category")%>
<% unless #categories.nil? %>
<ul><% #categories.each do |category| %>
<li><%= link_to category.category, :controller =>"microposts", :category => category.category, :method => 'category_list' %></li>
<% end %>
</ul>
<% end %>
After click the link enter category_list view.
category_list.html.erb
<h2>Catergory</h2>
<% #microposts.each do |post|%>
<article>
<h4><%= link_to post.title, post %>| <%= post.created_at %></h4>
</article>
<% end %>
<%= will_paginate #microposts %>
microposts controller
def category_list
#micropost = Micropost.select("category").where(params[:category])
#title = "Category"
end
Ok, try this:
UPDATED:
microposts_controller.rb
def category_list
#microposts = Micropost.where('category = ?', params[:category])
#categories = Micropost.select('DISTINCT(category)').map(&:category).compact
...
end
routes.rb
match '/microposts/:category' => 'microposts#category_list', :as => :microposts_category_list
_sidebar.html.erb
<h2>sidebar</h2>
<% unless #categories.empty? %>
<ul>
<% #categories.each do |category| %>
<li><%= link_to category, microposts_category_list_path(category) %></li>
<% end %>
</ul>
<% end %>
This is my code in the view. The Controller is simply getting the #category from the model.
This sort is not working. Ultimately I need it to sort alphabetically by name.
<%- #category.brands.sort_by{|brand| brand.name}.each do |brand| -%>
<li <%= "class='current'" if brand == #brand %>><%= link_to(brand.name, [#category, brand]) %></li>
<%- end -%>
Any ideas?
I would use the sort function directly:
<% #category.brands.sort { |a,b| a.name <=> b.name }.each do |brand| %>
<li <%= "class='current'" if brand == #brand %>>
<%= link_to(brand.name, [#category, brand]) %>
</li>
<% end %>
If you commonly sort by the same field
You can define the <=> method ( and optionally include Comparable ) on the model and just call model.sort and it should work.
in the model:
class Brand < AcvtiveRecord::Base
def <=> other
self.name <=> other.name
end
end
the view:
<% #category.brands.sort.each do |brand| %>
<li <%= "class='current'" if brand == #brand %>>
<%= link_to(brand.name, [#category, brand]) %>
</li>
<% end %>
If it wasn't an association I would sort it in the controller or have the model return it sorted then just display it with the view.
Then in the controller ( if this was not a subcollection )
#brands = Brand.all.sort
the view:
<% #brands.each do |brand| %>
<li <%= "class='current'" if brand == #brand %>>
<%= link_to(brand.name, [#category, brand]) %>
</li>
<% end %>
I am trying to make a simpel treelistmenu with ancestry.
Here is my code in view and it is not working:
<ul>
<% for cat_opg in CatOpg.roots %>
<li> <%= cat_opg.navn %><li>
<% for cat_opg in CatOpg.children %>
<li> <%= cat_opg.navn %><li>
</ul>
<% end %>
</ul>
<% end %>
And my controller:
def liste
#cat_opg = CatOpg.find(:all)
end
I want to make this simpel tree menu as:
Root
-children
Root
-children
I dont know what i am doing wrong.
PS: I am a rails beginner
First of all, you are getting to model in view, not to local variable.
Second, you're rewriting variable.
It should be something like this:
<ul>
<% cat_opg.roots.each do |cat_opg_root| %>
<li> <%= cat_opg_root.navn %><li>
<% cat_opg_root.children each do |cat_opg_child| %>
<li> <%= cat_opg_child.navn %><li>
</ul>
<% end %>
</ul>
<% end %>
Alex thank you for your answer.
Now it works
Controller: #cat_opg = CatOpg
And the view:
<ul>
<% #cat_opg.roots.each do |cat_opg_root| %>
<li> <%= cat_opg_root.navn %></li>
<% unless cat_opg_root.children.empty? %>
<ul>
<% cat_opg_root.children.each do |cat_opg_child| %>
<li> <%= cat_opg_child.navn %></li>
<% end %>
</ul>
<% end %>
<% end %>
</ul>