Is it possible to pass html elements into a partial? - ruby-on-rails-3

I have a partial that contains a header, a subheader and potentially one or more buttons. It is used on many different pages. Often the pages don't need buttons, so I just pass in the header and an optional subheader to the partial. However sometimes the pages need one or more buttons and the only way I've managed to allow for an arbitrary number of buttons to be passed in is using content_for. My partial looks like this:
<% if defined? page_title %>
<header class="pageHeader">
<div class="page-details">
<h3><%= page_title %></h3>
<% if defined? page_subtitle %>
<p><%= page_subtitle %></p>
<% end %>
</div>
<ul class="crud-menu nav-pills">
<%= content_for :page_header_buttons %>
</ul>
</header>
<% end %>
This use of content_for nasty. Is there any way I can pass the list items / buttons into this partial? How else could I deal with this situation?

You could transform this partial into a layout:
<% if defined? page_title %>
<header class="pageHeader">
<div class="page-details">
<h3><%= page_title %></h3>
<% if defined? page_subtitle %>
<p><%= page_subtitle %></p>
<% end %>
</div>
<ul class="crud-menu nav-pills">
<%= yield %>
</ul>
</header>
<% end %>
And you would render it like that:
<%= render layout: "your_partial_above", locals: { page_title: "page title } do %>
<%= render "partial_with_your_buttons" %>
<% end %>

Related

yield is not working in partials other than application.html.erb

Here I have a requirement to use yield in a partial other than application.html.erb , when I try this in partial it was showing me the blank value and if I try the yield in application.html.erb it was showing the value.
application.html.erb
body>
<div id="main-container">
<%= content_for?(:page_header) ? yield(:page_header) : (render :partial => "/shared/home_header") %>
<div id="middle-container">
<%= yield %>
</div>
<div id="bottom-container">
<%= render "/shared/bottom_partial" %>
</div>
index.html.erb
<% content_for :page_header do %>
<%= render :partial => "/shared/employer_header" %>
<% end %>
<% content_for :page_name do %>
<p>My Account</p>
<% end %>
<div>
xxxxxxxxxxxxxx
xxxxxxxxxxxxxx
</div>
/shared/_employer_header.html.erb
<div class="heading" >
<%= yield(:page_name) %>
</div>
When I am trying like above I am getting blank value in yield :page_name in employer header if I try the yield :page name in application.html.erb I am getting the value.Can any one help me to sort this out.Thank'U'.
Swap the order of your two content_fors so the :page_name is defined before the partial is rendered.
index.html.erb
<% content_for :page_name do %>
<p>My Account</p>
<% end %>
<% content_for :page_header do %>
<%= render :partial => "/shared/employer_header" %>
<% end %>

css: how to display two products in one row using each loop (rails)

Need some help in css.
i have an array of products and m displaying it using each loop. I wanna show two products in each row. do you know how to do it? currently i am using 50% width of div so two products will come in div.
Is there any other way?
Here is code:
<div id="product_list" >
<% if #products.size <= 0 %>
<h1/>No products found</h1>
<% else %>
<% #products.each do |p| %>
<div class="products">
<div><%= image_tag p.photos.first.avatar.url(:big) if p.photos.size > 0 %>
</div>
</div>
<% end %>
<% end %>
</div>
div product_list is main div
div products is 50%.
So it displays 2 div under main.
But this behaves weird when filters based on category etc. Is there any good way for this?
You can use
I many times handle such situation like this, try if it works for you.
pseudo code:
<div id="product_list" >
<% if #products.size <= 0 %>
<h1/>No products found</h1>
<% else %>
<ul><li class="products">
<% #products.each do |p| %>
<%= (count%2==0):'</li><li>':''; %>
<div><%= image_tag p.photos.first.avatar.url(:big) if p.photos.size > 0 %>
</div>
<% end %>
</li></ul>
<% end %>
</div>

Combining Multiple Models Into 1 View Rails

I am a beginner working on a RoR app that allows users to update their resume with experiences and education. I have two models for these different items and would like to display them together in chronological order. I want to know how to set this up in my profile controller and view.
I'd like to adopt this same practice to a separate part of the app that will combine post and discussion items from users in the same way. However, currently, my focus is on the experience and education portion.
profiles/show.html.erb:
<% if #experiences.any? or #educations.any? %>
<div class="postExpOuter">
<% #experiences.each do |experience| %>
<div class="postExp">
<div>
<h2><%= experience.position %></h2>
<h3><%= experience.company %> | <%= experience.location %></h3>
<h4>
<%= experience.start_month %> <%= experience.start_year %>
<% if experience.end_year %>
<%= " - " + experience.end_month %> <%= experience.end_year %>
<% else %>
<span> - Present</span>
<% end %>
</h4>
</div>
</div>
<% end %>
<% #educations.each do |education| %>
<div class="postExp">
<div>
<h2><%= education.degree %></h2>
<h3><%= education.school %></h3>
<h4>
<% if education.end_year %>
<span>Class of </span><%= education.end_year %>
<% else %>
<%= education.start_year %><span> - Present</span>
<% end %>
</h4>
</div>
</div>
<% end %>
</div>
<% end %>
just hacked in my browser, don't know if it works directly:
<%- (#experiences.to_a + #educations.to_a).sort_by(&:end_year).each do |item| -%>
<%- if item.is_a? Experience -%>
your markup here...
<%- else -%>
your other markup here...
<%- end -%>
<%- end -%>
haven't written ERB since... uhh, long. basically it's just the following: merge the 2 ActiveRecord-Relations as Arrays into one big Array, sort them by the timestamp you want (you may add .reverse at the end if you want). While iterating through the list, check the type of object you have.
Hope this helps.
profiles_controller.rb:
class ProfilesController < ApplicationController
def show
#user = User.find_by_profile_name(params[:id])
if #user
#posts = #user.posts.all(:order => "created_at DESC", :limit => 3)
#experiences = #user.experiences.all(:order => "start_year DESC")
#educations = #user.educations.all(:order => "start_year DESC")
#items = (#experiences.to_a + #educations.to_a).sort_by(&:start_year).reverse[0,3]
render action: :show
else
render file: 'public/404', status: 404, formats: [:html]
end
end
end
profiles/show.html.erb:
<% if #items.any? %>
<div class="postExpOuter">
<% #items.each do |item| %>
<% if item.is_a? Experience %>
<div class="postExp">
<div>
<h2><%= item.position %></h2>
<h3><%= item.company %> | <%= item.location %></h3>
<h4>
<%= item.start_month %> <%= item.start_year %>
<% if item.end_year %>
<%= " - " + item.end_month %> <%= item.end_year %>
<% else %>
<span> - Present</span>
<% end %>
</h4>
</div>
</div>
<%- else -%>
<div class="postExp">
<div>
<h2><%= item.degree %></h2>
<h3><%= item.school %></h3>
<h4>
<% if item.end_year %>
<span>Class of </span><%= item.end_year %>
<% else %>
<%= item.start_year %><span> - Present</span>
<% end %>
</h4>
</div>
</div>
<% end %>
<% end %>
</div>
<% end %>

Rails 3 Assign A Container Div Within Loop

say I'm looping through an array of products, how would I go about assigning a container div within that loop. For instance I want output like so:
<div class="page">
<p>product1</p>
<p>product2</p>
<p>product3</p>
<p>product4</p>
</div>
<div class="page">
<p>product5</p>
<p>product6</p>
<p>product7</p>
<p>product8</p>
</div>
I tried something like this:
<% #products.each_with_index do |product, index| %>
<% if index%4 == 0 %>
<div class="page">
<%end%>
<p><%= product.data %>
<% if index%4 == 0 %>
</div>>
<%end%>
<% end %>
But as expected it will only surround every 4th product with the container. I'm thinking that I'm going to need two loops in order for this to work, but how can I do this without duplicating data?
Edit
When using alternatives to each_with_index is there any way to keep a track of the index using this method? There are also some conditional attributes that get set based on the index value.
For example:
style=<%= index == 0 ? "top:5px;" : ""%>
<% #products.each_slice(4) do |slice| %>
<div class="page">
<% slice.each do |product| %>
<p><%= product.data %></p>
<% end %>
</div>
<% end %>
Another approach:
<% while group = #products.take 4 %>
<div class="page">
<% group.each do |product| %>
<p><%= product.data %></p>
<% end %>
</div>
<% end %>
The Rails method to split an array into groups of equal size is ary.in_groups_of(number)
<% #products.in_groups_of(4, false) do |group| %>
<div class="page">
<% group.each do |product| %>
<p><% product.data %></p>
<% end %>
</div>
<% end %>
Much nicer in Haml :)
- #products.in_groups_of(4, false) do |group|
.page
- group.each do |product|
%p= product.data
You can get the absolute product index with #products.index(product) or the group index with
<% #products.in_groups_of(4, false).each_with_index do |group, group_index| %>
<div class="page">
<% #products.each_with_index do |product, index| %>
<p><% product.data %></p>
<% if index % 4 == 3 %>
</div><div class="page">
<% end %>
<% end %>
</div>
(edit: forgot my end tag!)

Refactoring - Chapter 11, Exercise 3 Rails Tutorial 2nd Edition

Anyone else working through Chapter 11 Exercises for Michael Hartl's Rails Tutorial 2nd Edition?
Chapter 11, Exercise 3 asks:
Refactor Listing 11.31 by adding partials for the code common to the following/followers pages, the Home page, and the user show page.
I'm not seeing anything worth refactoring in the homepage, user show page, or the show_follow page
If anyone came up with something worthwhile for this exercise, would love to know.
Thanks!
You can refactor the first block of code from Listing 11.31:
<section>
<%= gravatar_for #user %>
<h1><%= #user.name %></h1>
<span><%= link_to "view my profile", #user %></span>
<span><b>Microposts:</b> <%= #user.microposts.count %></span>
</section>
because it is essentially the same as the views\shared_user_info.html.erb partial used on the home page (Listing 10.32). Therefore, you can replace the block of code above with this:
<%= render 'shared/user_info' %>
Note that you will also need to add <% #user ||= current_user %> to the top of the views\shared_user_info.html.erb partial (which is the same as what was necessary to add to the stats partial in Listing 11.20).
Additionally, there is some duplication (though not exact duplication) between the feed_item + feed partials with the user + micropost partials, where depending on which page is being displayed (follow_show, home, or profile) there are one or more elements being listed (name, gravatar, admin delete link, micropost content, micropost time stamp, and micropost delete link). Those could probably be refactored too to eliminate the feed_item+feed partials and replace them with a combination of the user + micropost partials depending on the page.
I just worked through this exercise and found a solution that works.
First I changed around app/views/shared/_user_info.html.erb to use the #user variable if it is set, and the current_user variable otherwise.
app/views/shared/_user_info.html.erb:
<% if #user %>
<%= link_to gravatar_for(#user, size: 52), #user %>
<h1>
<%= #user.name %>
</h1>
<span>
<%= link_to "view my profile", #user %>
</span>
<span>
<%= pluralize(#user.microposts.count, "micropost") %>
</span>
<% else %>
<%= link_to gravatar_for(current_user, size: 52), current_user %>
<h1>
<%= current_user.name %>
</h1>
<span>
<%= link_to "view my profile", current_user %>
</span>
<span>
<%= pluralize(current_user.microposts.count, "micropost") %>
</span>
<% end %>
Then I replaced the corresponding information in app/views/users/show_follow.hmtl.erb with the partial _user_info.html.erb
app/views/users/show_follow.hmtl.erb:
<div class="row">
<aside class="span4">
<section>
<%= render 'shared/user_info' %>
</section>
<section>
<%= render 'shared/stats' %>
<% if #users.any? %>
<div class="user_avatars">
<% #users.each do |user| %>
<%= link_to gravatar_for(user, size: 30), user %>
<% end %>
</div>
<% end %>
</section>
</aside>
<div class="span8">
<h3><%= #title %></h3>
<% if #users.any? %>
<ul class="users">
<%= render #users %>
</ul>
<%= will_paginate %>
<% end %>
</div>
</div>
I hope this answer helps anyone going through M. Hartl's tutorial.