How does rails get away with the following in an .erb file?
<%= yield :sidebar %>
<%= yield :other_bar %>
<%= yield :footer %>
How are they able to yield multiple times in the same context to different symbols? Is this some kind of rails magic?
I'm totally familiar with:
def some_method(arg1, arg2, &block)
yield(:block)
end
To my knowledge following doesn't work:
def some_incorrect_method(arg1, &block1, &block2)
yield(:block1)
yield(:block2)
end
So how are they doing it? How do they make it work?
They are passing a symbol into yield...
yield :symbol
...not yielding to a different block.
It works more like this:
def some_method(arg1, arg2, &block)
yield(:some_symbol1)
yield(:some_symbol2)
end
some_method do |symbol|
case symbol
when :some_symbol1
# do A
when :some_symbol2
# do B
else
# unrecognised symbol?
end
end
Do you mean http://apidock.com/rails/ActionView/Helpers/CaptureHelper/content_for ?
Related
I have helper function in application_helper.rb file:
def nested_attributes(attributes, cn = controller_name.classify)
attributes.map do |attribute, sub_attributes|
content_tag(:ul) do
content_tag(:li, :id => cn+"[#{attribute.id}]") do
raw(attribute.name+nested_attributes(sub_attributes))
end
end
end.join.html_safe
end
and then I calling it from view:
<%= nested_attributes #categories.arrange, 'baget_category_id' %>
But when I check result, I got Controller name (Which is default value) instead of 'baget_category_id'. When I remove default value, i got an error: wrong number of arguments (1 for 2). What I'm doing wrong?
Your problem seems you have to pass cn to recurring call:
raw(attribute.name+nested_attributes(sub_attributes, cn))
I am trying to reduce the repetitive code with the following pattern in an ERB template:
<% if content_for(some_key) %>
<%= yield(some_key) %>
<% else %>
Some default values here
<% end %>
I've tried defining the following method in ApplicationHelper but understandably it's not working as expected;
def content_for_with_default(key, &block)
if content_for?(key)
yield(key)
else
block.call
end
end
Here's how I'm trying to use it:
<%= content_for_with_default(some_key) do %>
Some default values here
<% end %>
How can I write the content_for_with_default helper so that it has the intended effect?
Your helper should be like this:
def content_for_with_default(key, &block)
if content_for?(key)
content_for(key)
else
capture(&block)
end
end
EDIT: difference between capture(&block) and block.call
After the erb file is compiled, the block will be some ruby code like this:
');#output_buffer.append= content_for_with_default('some_key') do #output_buffer.safe_concat('
');
#output_buffer.safe_concat(' Some default values here
'); end
You see, the strings within the block are concatenated to the output_buffer and safe_concate returns the whole output_buffer.
As a result, block.call also returns the whole output_buffer. However, capture(&block) creates a new buffer before calling the block and only returns the content of the block.
Sorry if this sounds obvious but i'm getting confused.
I'm trying to build a navigation list helper.
At the moment it receives a list of links from an array.
module ApplicationHelper
def site_pages
%w{index about contact promotions}
end
def nav_builder list
list.map do |l|
content_tag :li do
content_tag :a, :href => "#{l}_path" do
l
end
end
end
end
end
But when I run the page it ouput everything is the page as an array.
[<li>index</li> <li>about</li> <li>contact</li> <li>promotions</li>]
instead of displaying a list of links.
Is there anything special to do?
==EDIT==
I call my builder this way:
<%= nav_builder site_pages %>
The helper is expected to return a String of HTML, rather than an Array. Try joining the Array elements into a single String:
def nav_builder list
list.map do |l|
content_tag :li do
content_tag :a, :href => "#" do
"test"
end
end
end.join("\n").html_safe
end
Hey guys
I'm new to rails. I made this small test code for learning helper in rails:
apps/helpers/home_helper.rb
module HomeHelper
def show(var)
yield var
end
end
apps/views/home/index.html.rb
<%= show('hello world')%>
When I navigate to the url localhost:3000/home/index I got nothing in the html source
What did I do wrong?
There are few things to note here:
module HomeHelper
def show(var)
yield var
end
end
Firstly you're using yield which will pass control to the block given to the method. However, you then call the method without a block:
<%= show('hello world') %>
If you would have had a block it would have looked something like this:
<%= show('hello world') do |v| %>
<%= v %>
<% end %>
This would have output 'hello world' as you expected.
Most like you meant:
module HomeHelper
def show(var)
var
end
end
This returns the value you're passing in and will output it to the response stream.
While block helpers can often to be useful for drying up your code most of the time you want a partial with a layout.
why are you doing yield? Go with simple return:
def show(var)
var
end
Remove the yield. yield is meant for blocks - did you mean return? (it is optional).
Another (arguably better) option is to set the display text in your controller.
home_controller.rb
HomeController < ActionController
# Other controller code...
def index
#text = "Hello, world!"
end
end
index.html.erb
<%= #text %>
I'm trying a helper method that will output a list of items, to be called like so:
foo_list( ['item_one', link_to( 'item_two', '#' ) ... ] )
I have written the helper like so after reading Using helpers in rails 3 to output html:
def foo_list items
content_tag :ul do
items.collect {|item| content_tag(:li, item)}
end
end
However I just get an empty UL in that case, if I do this as a test:
def foo_list items
content_tag :ul do
content_tag(:li, 'foo')
end
end
I get the UL & LI as expected.
I've tried swapping it around a bit doing:
def foo_list items
contents = items.map {|item| content_tag(:li, item)}
content_tag( :ul, contents )
end
In that case I get the whole list but the LI tags are html escaped (even though the strings are HTML safe). Doing content_tag(:ul, contents.join("\n").html_safe ) works but it feels wrong to me and I feel content_tag should work in block mode with a collection somehow.
Try this:
def foo_list items
content_tag :ul do
items.collect {|item| concat(content_tag(:li, item))}
end
end
I couldn't get that work any better.
If you were using HAML already, you could write your helper like this:
def foo_list(items)
haml_tag :ul do
items.each do |item|
haml_tag :li, item
end
end
end
Usage from view:
- foo_list(["item_one", link_to("item_two", "#"), ... ])
Output would be correctly intended.
You could use content_tag_for, which works with collections:
def foo_list(items)
content_tag(:ul) { content_tag_for :li, items }
end
Update: In Rails 5 content_tag_for (and div_for) were moved into a separate gem. You have to install the record_tag_helper gem in order to use them.
Along with answers above, this worked for me well:
(1..14).to_a.each do |age|
concat content_tag :li, "#{link_to age, '#'}".html_safe
end
The big issue is that content_tag isn't doing anything smart when it receives arrays, you need to send it already processed content. I've found that a good way to do this is to fold/reduce your array to concat it all together.
For example, your first and third example can use the following instead of your items.map/collect line:
items.reduce(''.html_safe) { |x, item| x << content_tag(:li, item) }
For reference, here is the definition of concat that you're running into when you execute this code (from actionpack/lib/action_view/helpers/tag_helper.rb).
def concat(value)
if dirty? || value.html_safe?
super(value)
else
super(ERB::Util.h(value))
end
end
alias << concat