Rails: Displaying a nested list - ruby-on-rails

I have a property listing site with the following relationship structure
Region has_many :cities
City has_many :investor_cities
City has_many :investors through: :investor_cities
Investor has_many :investor_cities
Investor has_many :cities through: :investor_cities
An investor_city records cities that an investor prefers to buy in. It will be used to match properties with investors.
In the show view for an investor, I want to show cities by region that they are interested in.
In the controller
#regions = Region.all
In the view
<% #regions.each do |r| %>
<p><%= r.name %></p>
<ul>
<% r.cities.each do |c| %>
<% #inv_cty = InvestorCity.find_by_city_id_and_investor_id(c.id, #investor.id) %>
<% if #inv_city %>
<%= #inv_city.city.name %>
<% end %>
<% end %>
</ul>
<% end %>
However no investor_cities are rendered (despite there being two in the database).
I also tried this:
r.cities.investor_cities.each do...
But got this error.
undefined method `investor_cities' for #<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_City:0x007fb47e09b4d0>
Am at a bit of a loss so any ideas would be great. Thanks.
Edit - the below has worked. I would love to know how to do it a bit cleaner however. Thanks
<% #regions.each do |r| %>
<p><%= r.name %></p>
<ul>
<% r.cities.each do |c| %>
<% c.investor_cities.each do |ic| %>
<% if ic.investor_id == #investor.id %>
<li><%= ic.city.name %> <%= ic.investor.id %></li>
<% end %>
<% end %>
<% end %>
</ul>
<% end %>

Instead of use r.cities.investor_cities.each do...,
try using r.cities.select('investor_cities').where(investor_id: #investor.id).each do ...

Related

what should be the way to pass multiple activerecord object from controller to views?

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 %>

Ruby on Rails: How to fetch and display descendent records?

I have a parent model that has many children:
class Band < ActiveRecord::Base
has_many :concerts
end
class Concerts < ActiveRecord::Base
belongs_to :band
end
Now I would like to display them in my index view, but I can't figure out the syntax for displaying the children records:
<% #bands.each do |band| %>
Band name: <%= band.name %>
Concerts:
<ul>
<% #bands.concerts.each do |concert| %>
<%= concert.location %>
<% end %>
</ul>
<% end %>
I'm getting an error like undefined method 'concerts' for #<Array:0x00000102c537f0>. What is the proper way for fetching and displaying the descendent models?
You were really close, but you need to change #bands.concerts.each to band.concerts.each.
<% #bands.each do |band| %>
Band name: <%= band.name %>
Concerts:
<ul>
<% band.concerts.each do |concert| %>
<%= concert.location %>
<% end %>
</ul>
<% end %>

Nested output with related models

I have a model Category and a model Weblink. Category has_many Weblink and Weblink belongs_to Category. Now I want to show all categories in a view and within a category all weblinks belonging to that category, something link this:
<ul>
<% #categories.each do |category| %>
<%= category.category_name %>
<% #weblinks.each do |weblink| %>
<%= weblink.category_name link_to weblink.link_name, weblink.link_url %>
<% end %>
<% end %>
In the controller I have:
#categories = Category.all
#weblinks = Weblink.all
This shows every category and within every category all weblinks, instead of just the ones which belong to the specific category. How can I fix this?
Your view code should look like this
<% #categories.each do |category| %>
<%= category.name >
<% category.weblinks.each do |weblink| %>
<%= link_to weblink.name, weblink.link_url %>
<% end -%>
<% end -%>
It your controller, when querying for all the categories you should also include the weblinks model, something like this:
#categories = Category.all(:include => :weblinks)
Scope the inner loop to the outer category using the macro you get with has_many:
<% #categories.each do |category| %>
<%= category.category_name %>
<% category.weblinks.each do |weblink| %>
<%= link_to weblink.link_name, weblink.link_url %>
<% end %>
<% end %>

How can I refactor out needing so many for-loops in rails?

I need help refactoring this multi-loop thing. Here is what I have:
Campaign has_many Contacts
Campaign also has many Models which are templates: (Email, Call, and Letter).
Because I am looking for overdue on each, I created an array called Event which I'd like to loop through that contains ['email', 'call', 'letter'].
I need a list of all the Emails, Calls and Letters that are "overdue" for every Contact that belongs to a Campaign. Overdue is determined by a from_today method which looks at the date the Contact was entered in the system and the number of days that needs to pass for any given Event. from_today() outputs the number of days from today that the Event should be done for a given Contact.
Here is what I've done, it works for all Emails in a Campaign across all contacts. I was going to try to create another each do loop to change the class names.
Wasn't sure where to begin: named_scope, push some things into a method, etcetera, or -- minimum to be able to dynamically change the class names so at least it loops three times across the different events rather than repeating the code three times:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<% #events.each do |event| %>
<%= event %>
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% for contact in campaign.contacts.find(:all, :order => "date_entered ASC" ) %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Just to add to Patrick's answer, I would also use the :collection option of render to simplify this a bit further, e.g. have a partial _contact.html.erb to render each contact:
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
and then render the contacts collection with
= render :partial => "contact", :collection => #contacts
I also wouldn't do a find in the view, instead I would setup all the variables in the controller, and probably move all the conditional code into a helper. It's preferable to keep as much logic as possible out of the views.
I'd put the output for each resource into a partial, like so:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<%= render 'events', :events => campaign.events %>
<% end %>
then in app/views/campaigns/_events.html.erb
<% events.each do |event| %>
<%= event %>
<%= render 'emails', :emails => event.emails %>
<% end %>
then in app/views/campaigns/_emails.html.erb
<% emails.each do |email| %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<%= render 'contacts', :contacts => email.contacts.all(:order => "date_entered ASC", :email => email) %>
<% end %>
then in app/views/campaigns/_contacts.html.erb
<% contacts.each do |contact| %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>

Ruby on Rails Nesting Blocks

I have a block that I need to nest another block in. Or maybe I don't and I am doing this totally wrong.
There is a UNIT and the UNIT has_many COMPANIES
Then COMPANIES has_many USERS
on the UNIT show page I am trying to do something like this:
<% #company.each do |c|%>
<%= c.name %>
<% ??? each do |f| %>
<p>
Name: <%= f.name %>
</p>
<% end %>
<% end %>
So basically for each Company I show the name of the Company, no problem there, but then I am trying to show that within each company there are particular Users I am listing that belong to that company. I can't define that in the controller because there are multiple companies.
That user is:
cc = #user.find(:all, :conditions => ["position = ?", "Company Commander"])
I am not sure how to loop through for each Company and then for that Company loop through and list a particular user for that company.
Sorry if I did a poor job of explaining this. I am not sure if this is even the right way to be doing this.
Thanks in advance.
Try this:
<% #company.each do |c| %>
<%= h(c.name) %>
<% c.users.each do |u| %>
<p>Name: <%= h(u.name) %></p>
<% end %>
<% end %>
Firstly, you need to have relations defined in your model:
# Unit model
has_many :companies
has_many :users, :through => :companies
# Company model
has_many :users
belongs_to :unit
...
Than in your Unit controller:
#unit = Unit.find(params[:id]) # or something similar
And in your view:
<% #unit.companies.each do |c|%>
<%= c.name %>
<% c.users each do |f| %>
<p>
Name: <%= f.name %>
</p>
<% end %>
<% end %>
It seems you want to list the users with a position of 'Company Commander' associated with each company.
Maybe a named scope on user would help:
class User < ActiveRecord::Base
...
named_scope :company_commanders, :conditions => "position = 'Company Commander'"
end
Now you can loop through them as you see fit.
<% #company.each do |c|%>
<%= c.name %>
<% c.users.company_commanders each do |f| %>
<p>
Name: <%= f.name %>
</p>
<% end %>
<% end %>
if a company has many users, then you should be able to just do 'company.users' to get the list of users associated with that company. then you can loop through or list those however you like.
Assuming you set up the relationship in your models, this is just how active record sets up these associations for you.
This is what ending up working:
named_scope :company_commanders, :conditions =>{:position => 'Company Commander'}
<% #company.each do |c| %>
<%= h(c.name) %><br />
<% if c.users.company_commanders.blank? %>
<%= link_to "Add this User", new_user_path %><br />
<% else %>
<% c.users.company_commanders.each do |u| %>
<p>Name: <%= h(u.name) %></p>
<% end %>
<% end %>
<% end %>
</p>

Resources