Passing markup into a Rails Partial - ruby-on-rails

Is there any way of doing something equivilant to this:
<%= render partial: 'shared/outer' do %>
<%= render partial: 'shared/inner' %>
<% end %>
Resulting in
<div class="outer">
<div class="inner">
</div>
</div>
Obviously there would need to be a way of marking up 'shared/outer.html.erb' to indicate where the passed in partial should be rendered:
<div class="outer">
<% render Here %>
</div>
In my specific case I have a generic page header, consisting of a header and subheader, that is shared across all pages, but would like the option of passing in page-specific markup to that header to be rendered below the title and subtitle.

I'd use content_for :
<% content_for :subheader do %>
<%= render partial: 'shared/inner' do %>
<% end %>
<%= render partial: 'shared/outer' %>
Then in shared/outer :
<div class="outer">
<%= yield(:subheader) %>
</div>
You can put whatever you'd like in the content_for block and use it as many times as you'd like, just change the key name (here subheader)

Related

Rails yield and content_for wieird behaviour, `yield :filter` only work if placed after default yield

Lets say i have this partial i am trying to render
#layouts/_subheader.html.erb
<div class="subheader">
<%= yield %>
<%= yield :filters %>
</div>
when i use this partial in a view like this
<%= render 'layouts/sub_header' do %>
<h2> Content For Yield </h2>
<% content_for :filters do %>
<h2> Content for Filters </h2>
<% end %>
<% end %>
i am getting the HTML output as
<div class="subheader">
<h2> Content For Yield </h2>
<h2> Content for Filters </h2>
</div>
this works as expected, but the problem arises when i change the order of the yield tags in the partial
instead of the above, if i rewrite the partial as
#layouts/_subheader.html.erb
<div class="subheader">
<%= yield :filters %>
<%= yield %>
</div>
i am getting output as
<div class="subheader">
<h2> Content For Yield </h2>
</div>
the content_for :filters is not being rendered.
what an i doing wrong here ? is this the correct behavior or am i doing something wrong ?
what should i do if i have to make the content of the yield :filters appear before the plain yield
EDIT:-
I have tried
#layouts/_subheader.html.erb
<div class="subheader">
<%= content_for :filters %>
<%= yield %>
</div>
but it seems to be not working as well.
Thanks everyone on reddit and #Ezhil i found a solution, this is what i did
what i did is i placed the content_for capture group before the render like this
#index.html.erb
<% content_for :filters do %>
<h2> Content for Filters </h2>
<% end %>
<%= render 'layouts/sub_header' do %>
<h2> Content For Yield </h2>
<% end %>
and in my partial
#layouts/_subheader.html.erb
<div class="subheader">
<%= content_for :filters %>
<%= yield %>
</div>
It's because the block isn't executed until the first yield, so there's no content captured until that runs.

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.

How to group and dry common view code in Rails

I have two models Episode and Playlist with the following two views:
For Episode:
<div class="name">
<%= link_to episode.name, episode_path(episode) %>
</div>
<div class="description">
<%= link_to episode.description episode_path(episode) %>
</div>
For Playlist:
<div class="name">
<%= link_to playlist.name, playlist_path(playlist) %>
</div>
<div class="description">
<span>Some special span</span>
<%= raw(playlist.description) %>
</div>
The common things between this two views are the
<div class="name">. <div class="description">
but the content of this two divss is different for each view.
Question: How could I extract this two common divs into another view, called _section?
What have I tried:
Cells -> they give me a nice view model with properties, but I could not find a way to added additional HTML elements between the places where the properties will be put.
Yield and Content_for-> I have tried to create a separate _section layout with:
_section.html.erb
<div class="name">
<%= yield :name %>
</div>
<div class="description">
<%= yield :description %>
</div>
and then two views. The Episode view will render the following
<% render layout: "section" do %>
<% content_for :name do %>
<%= link_to episode.name, episode_path(episode) %>
<% end %>
<% content_for :description do %>
<%= link_to episode.description episode_path(episode) %>
<% end %>
<% end %>
This kind of works until you get to caching (they said content_for does not work with caching and I need to cache this) and the other problem I had was that you should include an additional empty not named yield.
I ended up with moving all the logic for playlist and episode views into a single view - called section that basically does the following:
<div class="name">
<%= link_to object.name, polymorphic_path(object) %>
</div>
<div class="description">
<% if is_episode? %>
<h1>Some special h1 for episode</h1>
<% elsif is_playlist? %>
<h2>Some special other html for playlist for episode</h2>
<% end %>
<%= raw(playlist.description) %>
</div>
Wow those "ifs" are ugly. I am not sure if I understood the problem correctly, but it is surprising I could not find a cleaner solution.

Partial with multiple yields in rails

I'm looking for solution to have partial with multiple yields.
In real example I have this views structure:
Basic application.erb (/views/layouts/application.erb):
<!DOCTYPE html>
<head>
<title>Some title</title>
</head>
<body>
<div class="page">
<%= yield %>
</div>
</body>
</html>
Some partial to DRY my code (/views/shared/content.erb):
<div class="content">
<div class="sidebar">
<%= yield :sidebar %>
</div>
<div class="main">
<%= yield %>
</div>
</div>
And controller view (/views/home/index.erb):
<%= render :partial => 'layouts/header' %>
<%= render :partial => 'shared/navigation' %>
<% # It is close to what I want to do %>
<%= render :layout => 'shared/content' do %>
<% content_for :sidebar do %>
<%# This is will go to application.erb, not in content.erb %>
<%= render :partial => 'shared/menu' %>
<% end %>
<%= yield %>
<% end %>
<%= render :partial => 'layouts/footer' %>
So the main issue here is to have a template block with multiple yield areas and ability to pass custom html or render another partial.
This question is old, however, it's still relevant when I was searching for an answer on Google.
I've come up with a solution, while still not beautiful, works very well. The idea uses Rails' capture method, which takes a block and stores its contents into a variable:
controller.html.erb
<%= render 'shared/partial', body: capture { %>
My body content
<% }, footer: capture { %>
My footer content
<% } %>
shared/_partial.html.erb
<div id="body"><%= body %></div>
<div id="footer"><%= footer %></div>
Hope this helps someone!
In my case I've found solution like this.
On my controller view (/views/home/index.erb):
<% sidebar_content = render :partial => 'shared/slider' %>
<%= render :layout => 'shared/content', :locals => {:sidebar => sidebar_content} do %>
<%= yield %>
<% end %>
The partial with multiple areas (/views/shared/content.erb):
<div class="content">
<div class="sidebar">
<%= sidebar %>
</div>
<div class="main">
<%= yield %>
</div>
</div>
This solution doesn't look pretty, but it works. I hope to find something better in near future.
Here is my solution:
/views/shared/_content.erb
<div class="content">
<div class="sidebar">
<%= yield :sidebar %>
</div>
<div class="main">
<%= yield %>
</div>
</div>
views/home/index.erb
<%= my_content_tag do %>
<% content_for :sidebar do %>
<%= render :partial => 'shared/menu' %>
<% end %>
<%= yield %>
<% end %>
app/helpers/my_tags_helper.rb
module MyTagsHelper
def my_content_tag(&block)
sidebar_backup = #view_flow.content.delete(:sidebar)
x = capture(&block)
html = render('shared/content') { x }
#view_flow.content[:sidebar] = sidebar_backup if sidebar_backup
html
end
end
The key is using capture to extract content_for block and pass it into #view_flow

Writing a helper method for page headers

I am currently integrating twitter bootstrap.
My application layout currently includes my flash messages right above the yield:
<div class="container">
<%= render 'layouts/header' %>
<div class="content" role="main">
<%= render :partial => 'shared/flash', :object => flash %>
<%= yield %>
</div>
<%= render 'layouts/footer' %>
</div> <!--! end of #container -->
However, I am also using the bootstrap Page Headers in my individual views
<div class="page-header">
<h1>Sign In <small>through one of these services:</small></h1>
</div>
I would like my flash messages to be below these page headers when page headers are set. So I'm thinking the best way to do this is to create a helper method and edit my application layout to be something like:
<%= render 'layouts/header' %>
<div class="content" role="main">
<%= pageHeader %>
<%= render :partial => 'shared/flash', :object => flash %>
<%= yield %>
</div>
How would I go about writing the helper for this. Would the best way to just have instance variables for #page_header and #page_header_small in my controller? Or is there a better rails way to handle this situation?
You can user content_for combined with yield.
In your layout you can put this block with headline above flash message
<%= yield :page_header %>
<%= render :partial => 'shared/flash', :object => flash %>
<%= yield %>
and in your template for action
<% content_for :page_header do %>
<div class="page-header">
<h1>Sign In <small>through one of these services:</small></h1>
</div>
<% end %>
Check it out at Ruby Guides

Resources