Combine 2 models into one array, paginate, and force 1 partial - ruby-on-rails

I want to combine data from two different models, combine it into one array and use the will_paginate gem to paginate the results in my view on that collection. I'm already using require 'will_paginate/array' to allow for a normal, non-ActiveRecord array and its all working properly.
My problem is that its using two different partials to render the entries based on which model the data originated from. How do I force it render one specific partial?
controller
def sample_action
page = params[:page] || 1
#collection = []
#car.messages.select {|msg| #collection << msg}
Alerts.where(car_id: #car.id).select {|alerts| #collection << alerts}
#collection = #collection.sort{|a,b| b[:created_at] <=> a[:created_at]}
#collection = #collection.paginate(:page => page, :per_page => 30)
respond_to do |format|
format.html { render :sample_action }
end
end
view
<% if #collection.count > 0 %>
<div class="row">
<div class="col-xs-12">
<div id="car-alerts" class="car-alerts-all">
<%= render #collection -%>
</div>
</div>
</div>
<%= will_paginate #collection -%>
<% else %>
<p class="no-alerts-message">Your car is healthy</p>
<% end %>
car partial
Car - <%= collection.id %>
<br>
alert partial
Alert - <%= collection.id %>
<br>

I would use the fact that you basically just want to output the model name and do this:
<% if #collection.count > 0 %>
<div class="row">
<div class="col-xs-12">
<div id="car-alerts" class="car-alerts-all">
<%- #collection.each do |obj| -%>
<%= obj.class.to_s -%> - <%= obj.id %>
</div>
</div>
</div>
<%= will_paginate #collection -%>
<% else %>
<p class="no-alerts-message">Your car is healthy</p>
<% end %>
but it would break easily if you wanted to add labels different to class names.

You need to iterate over each item in the collection and have a conditional based on the model type, so for example:
<div id="car-alerts" class="car-alerts-all">
<% #collection.each do |car_or_alert| %>
<%=
if car_or_alert.is_a?(Alert)
render :alert #you need to add locals here as well, but it unclear what locals you are using
else
render :car #see above
end %>
<% end %>
</div>

Turns out it was as simple as setting the partial from render
<% if #collection.count > 0 %>
<div class="row">
<div class="col-xs-12">
<div id="car-alerts" class="car-alerts-all">
<%= render collection: #collection, partial: 'car/partial_name' -%>
</div>
</div>
</div>
<%= will_paginate #collection -%>
<% else %>
<p class="no-alerts-message">Your car is healthy</p>
<% end %>
This being the key change -
<%= render collection: #collection, partial: 'car/partial_name' -%>

Related

Reusing a partial in the same page

I have a controller with 3 objects and i want to call the same partial three time with each object
class HomeController < ApplicationController
def index
#evaluations_teams = Evaluation.eager_load(:user).where(users: {status: true}).where(team_id: current_user.student_details.last.team_id).all.order(created_at: :desc).limit 5
#evaluations_stage = Evaluation.eager_load(:user).where(users: {status: true}).where(stage_id: current_user.student_details.last.stage_id).all.order(created_at: :desc).limit 5
#evaluations_schools = Evaluation.eager_load(:user).where(users: {status: true}).where(school_id: current_user.student_details.last.school_id).all.order(created_at: :desc).limit 5
end
end
INDEX.HTML.ERB
<%= render 'home/partials/loop_areas', locals: {evaluations:#evaluations_teams} %>
<%= render 'home/partials/loop_areas', locals: {evaluations:#evaluations_stage} %>
<%= render 'home/partials/loop_areas', locals: {evaluations:#evaluations_schools} %>
Partial: home/partials/_loop_areas.html.erb
<% #evaluations.each do |evaluation| %>
<div class="div-card-infosaluno">
<div class="card-aluno"><%= evaluation.user.full_name %></div>
<div class="card-serie"><%= "#{evaluation.stage.name} · #{evaluation.school.name}" %></div>
</div>
<% end %>
This returns:
undefined method `each' for nil:NilClass
on <% #evaluations.each do |evaluation| %>
How can i do this?
You're trying to access an instance variable #evaluations in your partial but it is not defined in the controller action.
You've to instead loop through the local variable evaluations.
<%# home/partials/_loop_areas.html.erb %>
<% evaluations.each do |evaluation| %>
...
<% end %>
And your view should be
<%= render 'home/partials/loop_areas', evaluations: #evaluations_teams %>
<%= render 'home/partials/loop_areas', evaluations: #evaluations_stage %>
<%= render 'home/partials/loop_areas', evaluations: #evaluations_schools %>
You can also pass a hash to the render method but I feel it is more verbose.
<%= render partial: 'home/partials/loop_areas', locals: { evaluations: #evaluations_schools } %>
Please try the below code in Partial: home/partials/_loop_areas.html.erb
It should work as you are expecting.
<% evaluations = local_assigns[:evaluations] %>
<% evaluations.each do |evaluation| %>
<div class="div-card-infosaluno">
<div class="card-aluno"><%= evaluation.user.full_name %></div>
<div class="card-serie"><%= "#{evaluation.stage.name} · #{evaluation.school.name}" %></div>
</div>
<% end %>
Thanks.

Trying to make my partial work

I need to iterate through two sets of values on the same web page so I wrote a partial to eliminate duplicate code. The two variables that I am trying to iterate through are #bookmarks and #liked_bookmarks.
The partial is rendered properly if I use either #bookmarks or #liked_bookmarks in the partial it's self but then the only one of the value sets are displayed. in the code below I tried using #resource as a stand in that will have #bookmarks and #liked_bookmarks passed into it.
this is the partial that is located in app/views/bookmarks/_bookmarksandlikes.html.erb
<div>
<% #resource.each do |x| %>
<%= x.url%>
<% end %>
<div>
and this is the code for the page that I am calling the above partial on located in app/views/users/show.html.erb
<h1>My Blocmarks</h1>
<p>Find me in app/views/users/show.html.erb</p>
<div>
<h1>Blockmarks I have created </h1>
<div class =row>
<%= render partial: 'bookmarks/bookmarksandlikes', locals: { topic: #bookmarks} %>
</div>
</div>
<div>
<h1>Blockmarks I have liked
<%= render partial: 'bookmarks/bookmarksandlikes', locals: { topic: #liked_bookmarks} %>
</h1>
</div>
You're close. Instead of #resource just use topic, which you are passing in as a local.
<div>
<% topic.each do |x| %>
<%= x.url%>
<% end %>
<div>
Since you are not passing #resource into partial and passing only topic variable you need to do it
<div>
<% topic.each do |x| %>
<%= x.url%>
<% end %>
<div>
Instead of:
<div>
<% #resource.each do |x| %>
<%= x.url%>
<% end %>
<div>
Do it:
<div>
<% topic.each do |x| %>
<%= x.url%>
<% end %>
<div>
Because you pass the object on locals: { topic: #liked_bookmarks} you should use topic instead of using any instance that is not initialize.

Group posts by Year - Rails

I have model called "Shoes" that belongs to the a model called "History". I want to show all of the shoes in my History show.html.erb page, but I want to group them by their release date. (I have an attribute called release for that).
I'm using this example from Railscasts:
shoes_controller
def index
#shoes = Shoe.all(:order => 'release, name')
#release_year = #shoes.group_by { |t| t.release.beginning_of_year }
end
Shoes index.html.erb view
<% #release_year.each do |release, shoes| %>
<% shoes.each do |shoe| %>
<%= shoe.name %>
<% end %>
<% end %>
I want to be able to do this but on my History show.html page, how can i accomplish this?
What I have so far:
History show.html.erb
<% #history.shoes.each do |shoe| %>
<div class="shoe">
<%= shoe.name %>
</div>
<% end %>
Thanks.
Perhaps you may want to create a _shoes_by_year partial in the shoe views, and then render the shoe collection:
views/shoes/_shoe.html.erb
<div class=shoe>
<%= shoe.name %>
</div>
views/shoes/_shoes_by_year.html.erb
<% shoes_by_year.each do |release_year, shoes| %>
<div>
<h3><%= release_year.year %></h3>
<%= render 'shoes/shoe', collection: shoes %>
</div>
<% end %>
histories_controller.rb
def show
...
#shoes = #history.shoes.group_by { |shoe| shoe.release.at_beginning_of_year }
end
/views/history/show.html.erb
...
<%= render 'shoes/shoes_by_year', object => #shoes %>

How can I pass more than 2 arguments to partial?

I want to pass 2 arguments such as topic and icon_photo.
How can I do that?
undefined method `icon_photo?'
I got this error with the code below.
view
<div class="Topic">
<% #community.topics.each do |topic| %>
<%= render 'topics/topic', :topic => topic, :icon_photo => topic.user.profile.avatar %>
<% end %>
</div>
You can pass a locals hash:
<div class="Topic">
<% #community.topics.each do |topic| %>
<%= render 'topics/topic', locals: {topic: topic, icon_photo: topic.user.profile.avatar, etc: 'blabla' } %>
<% end %>
</div>
See some documentation here:
http://www.tutorialspoint.com/ruby-on-rails/rails-render.htm
A little improvement can be mabe, you can render your collection like this:
<div class="Topic">
<%= render partial: 'topics/topic', collection: #community.topics %>
</div>
# in your partial topics/_topic.html.erb:
<% icon_photo = topic.user.profile.avatar %>

will_paginate - don't show page entries info if collection is empty or less than per-page param

Let's say I have a controller action that creates the WillPaginate collection:
#comments = WillPaginate::Collection.new(#page_num, 15, #comments.length).concat(comments_to_paginate)
Then in my view:
<div class="pag">
<div clas="page_info">
<%= page_entries_info #comments %>
</div>
<%= will_paginate #comments, :container => false %>
</div>
Now what I want to do is to NOT show the page_entries_info output if (1) there are no comments and (2) if the number of entries (eg, 7) is less than the per page limit (15).
How would you go about handling this?
You just need to guard your page_entries_info with the conditions you wish
For example
<div class="pag">
<% if #comments.length > 0 && #comments.total_pages > 1 %>
<div class="page_info">
<%= page_entries_info #comments %>
</div>
<% end %>
<%= will_paginate #comments, :container => false %>
</div>
Or you can put this in your controller and keep your view a little cleaner, also letting you have one variable to reuse if you need the same guard around other parts of view code.
#comments = WillPaginate::Collection.new(#page_num, 15, #comments.length).concat(comments_to_paginate)
#show_pagination = #comments.length > 0 && #comments.total_pages > 1
Then in the view:
<div class="pag">
<% if #show_pagination %>
<div class="page_info">
<%= page_entries_info #comments %>
</div>
<% end %>
<%= will_paginate #comments, :container => false %>
</div>
If you can deal with the extra div, then this should also work
<div class="pag">
<div class="page_info">
<%= page_entries_info(#comments) if #show_pagination %>
</div>
<%= will_paginate #comments, :container => false %>
</div>

Resources