Rendering content inside a partial via =yield - ruby-on-rails

I'm creating an application with ruby on rails where I have an items/_item.html.erb. Inside the partial is a yield statement so i can add extra content as needed. In this case, I want to add a specific button to item depending on what view calls partial.
This is what I've tried and it renders the partial, but it does not render the block:
_item.html.erb
<%= yield if block_given? %>
<div>
<%= item.name %>
</div>
someview.html.erb
...
<% render(:partial => 'items/item', :collection => current_user.items do %>
<%= "HELLO" %>
<% end %>
...
I have also tried using content_for and some other stuff with no success. Is there a way to be able to render specific content inside a partial via yield? I'm currently using Rails3
EDIT:
I've found out that it's the :collection hash that makes it impossible insert the block.
Both of this pieces of code work:
<%= render :layout => 'items/item' do %>
Hello world
<% end %>
<%= render :layout => 'items/item', :locals => {:item => current_user.items.first} do %>
Hello world
<% end %>
This means that if i do a .each i could accomplish what I want but it would be ugly code. Anyone know a way around this?

content_for should work fine in this case. Here is the code I just double checked locally.
somewhere.html.erb
<% content_for :foobar do %>
fubar
<% end %>
_item.html.erb
<% if content_for? :foobar %>
<%= yield :foobar %>
<% end %>

Related

Rails: How to make small changes in different views

Is it possible to make small changes in different views?
The same partial is rendered in index.html.erb and show.html.erb as below.
index.html.erb
<%= render #schedules %>
show.html.erb
<%= render #schedules %>
What I'd like to do is not to display some value in the index.html.erb. (and display some value in both erb)
For example, I'd like to display start_at and end_at only in show.html.erb and display title in both erb.
_schedule.html.erb
<% schedule.rooms.each_with_index do |a, idx| %>
<% a.events.each do |e| %>
<%= l(e.start_at) %>-<%= l(e.end_at) %> # display only show.html.erb
<%= e.title %> #display both erb
...
<% end %>
...
<% end %>
Althogh I come up with idea which I create two partials, it contradicts the DRY policy.
It would be appreciated if you could give me any idea.
You can use controller.action_name.
<% if controller.action_name == 'show' %>
<%= l(e.start_at) %>-<%= l(e.end_at) %> # display only show.html.erb
<% end %>
The params hash also contains the action_name.
action_name is enough and do the trick but personally I don't like this. I'd do two separate partials.
Can check current action and current controller on page. So we can call single partial from different actions and can customize as per action name or action and controller name.
eg.
<% schedule.rooms.each_with_index do |a, idx| %>
<% a.events.each do |e| %>
<% if #current_controller == "events" and #current_action == "show" %>
<%= l(e.start_at) %>-<%= l(e.end_at) %> # display only show.html.erb
<% end %>
<%= e.title %> #display both erb
...
<% end %>
...
<% end %>
Also need to update Application Controller.
class ApplicationController < ActionController::Base
before_filter :instantiate_controller_and_action_names
def instantiate_controller_and_action_names
#current_controller = controller_name
#current_action = action_name
end
end
You could use CSS to hide/show the content based on context.
In practice, I have found this a good way to reuse partials that have small differences. Especially when those differences don't cost anything to compute i.e. printing a date
You can cache the partials without worrying about where they are rendered
Reduce conditional logic
Remove duplication
<% if controller.action_name == 'show' %> is fine for a simple use case. If/When you come to have multiple places where the partial needs to be rendered, it will become unwieldy. The CSS solution would only require another wrapper <div class="schedules--whatever"> and the related CSS style.
show.html.erb
<div class="schedules--show">
<%= render #schedules %>
</div>
index.html.erb
<div class="schedules--index">
<%= render #schedules %>
</div>
_schedule.html.erb
<% schedule.rooms.each_with_index do |a, idx| %>
<% a.events.each do |e| %>
<div class="event__date">
<%= l(e.start_at) %>-<%= l(e.end_at) %>
</div>
<%= e.title %>
...
<% end %>
...
<% end %>
schedules.css
.schedules--show .event__date {
display: block;
}
.schedules--index .event__date {
display: none;
}

Passing in variable to rails partial

Right now I have a rails partial that looks like this:
<%= render :partial => "/talk/partials/comment", :collection => #comments, :locals => {:votes => #votes} %>
I am passing in a collection of comments and another local variable.
That comment partial then goes right into using the comment variable and works fine.
I have since made another partial called '/talk/partials/comment_2014'. When I try this, I am getting the error undefined local variable or method 'comment'. From what I can gather, when I have a different partial name, something with the variable also changes. I would like to keep the same comment variable for the new partial ''/talk/partials/comment_2014'. How would I go about doing this?
Something I tried which did not work was the following:
<% #comments.each do |comment| %>
<%= render :partial => "/talk/partials/comment_2014", comment: comment, :locals => {:votes => #votes} %>
<% end %>
which did not work either.
You can do it this way
<% #comments.each do |comment| %>
<%= render "/talk/partials/comment_2014", comment: comment, votes: #votes %>
<% end %>
Or
<% #comments.each do |comment| %>
<%= render partial: "/talk/partials/comment_2014", locals: { comment: comment, votes: #votes } %>
<% end %>
Notice in the second way the comment is inside the locals.

Rendering a partial using specific controller variables

I am trying to play with Rails naming conventions as in here and render two different pages with different variables using one partial.
index
<%= render #events_future %>
current
<%= render #events_current %>
event controller
def index
#events_future = ...
end
_event.html.erb
<% #events.each do |event| %>
...
<% end %>
I get the undefined "each" method
Please point me in the right direction
I think the best thing to do here is to pass a locals to the partial _event.html.erb because the partial needs to display different objects like follows:
index
<%= render 'event', events: #events_future %>
current
<%= render 'event', events: #events_current %>
In the above two render statements, the events gets passed to the event partial as a local.
Then in your _event.html.erb you would do:
<% events.each do |event| %>
...
<% end %>
Do you have #events initialized in your controller?
I see that you have #events_future and #events_current, but if #events is not defined in the controller, your view wouldn't know what you are referring to.
If you want to reuse events for both future and current, use the following in each view
<!-- index.html.erb -->
<%= render 'event', events: #events_future %>
<!-- current.html.erb -->
<%= render 'event', events: #events_current %>
This renders the _event.html.erb partial and sets the events local variable. In _event.html.erb, use
<% events.each do |event| %>
<!-- do stuff -->
<% end %>
You have to pass the variable to the partial when you render it:
render :partial => 'event', :locals => {:events => #events_current} %>
render :partial => 'event', :locals => {:events => #events_future} %>
And then in your partial you do:
<% events.each do |event| %>
...
<% end %>

check if Rails partial is empty

I have a main page that is responsible for HTML/CSS styling, but some of the contents come from partials. A partial receives some locals or params, i.e. current_user or person, and displays information if any.
Is there a way for me to check if a partial rendered anything? My end goal is something like this:
<% if my_partial can render something %>
<div class="css_for_something">
<%= render(partial: 'my_partial', locals: {...} ) %>
<% else %>
<div class="css_for_no_info">
<%= render something else %>
<% end %>
I do not want the partials to handle styling logic; they just need to display content if any. Conversely, the main page should not know anything about the logic in the partial(s), such as checking values or querying the database.
Thank you
Unfortunately, Chris Peter's solution did not work for me on rails 4.2.4, as render_to_string seems to not be available in views.
However, the following worked (rails 4.2.4):
<% partial_content = render partial: 'my_partial' %>
<% if partial_content.present? %>
<%= partial_content %>
<% else %>
<%# rendered if partial is empty %>
<% end %>
Be aware that the present? check really only checks if what was rendered is empty. If, something, e.g. a HTML comment, is returned, the check returns false.
Try storing the value generated by render_to_string in a variable:
<% partial_content = render_to_string(partial: 'my_partial', locals: {...} ).strip %>
Then you can see if it contains any content:
<% if partial_content.present? %>
<%= partial_content %>
<% else %>
<div class="css_for_no_info">
<%= render something else %>
</div>
<% end %>

How do you find out what controller/action you are in via ruby code?

I have a different subheader partial I want to render dependent on where I'm at in my application. How do I go about determining where I'm at via ruby? Or do I need to parse the URL?
Example :
If I'm at my root level I want to use /home/subheader, if I'm in controller 'test' I want to render /test/subheader, etc... etc...
basically looking for this part:
(in my application view)
<%- if ############ %>
<%= render :partial => '/home/subheader' %>
<%- elsif ########### %>
<%= render :partial => '/test/subheader' %>
<%- else %>
<%= render :partial => '/layouts/subheader' %>
<%- end %>
Thanks
You can use current_page?
if current_page? :controller => 'home', :action => 'index'
do_this
end
or use the controller's method controller_name
if controller.controller_name == 'home'
do_that
end
If you're using this in a per-controller basis, you should probably need layouts or use different templates, rendering different partials depending in controller/action is a code smell.
P.S: You could also try to get the params[:controller] and params[:action] variables, but I am not sure if they are passed correctly if your route is non the standard /:controller/:action
A slightly easier way to manage this would be to use content_for. For example:
#app/layouts/application.html.erb
<html>
<body>
<h1>My Application</h1>
<%= yield(:subheader) || render(:partial => 'layouts/subheader') %>
<%= yield %>
</body>
</html>
This layout will first try to render the subheader content that was passed in from the view, otherwise it will render the partial 'layouts/subheader'. Then in each view that requires a custom subheader, all you have to do is:
#app/views/home/index.html.erb
<% content_for :subheader, render(:partial => 'subheader') %>
And in your other controller, you could use something completely different, like:
#app/views/other/show.html.erb
<% content_for :subheader do %>
<h2>A different subheader</h2>
<% end %>

Resources