Viget Labs posted an article and gist detailing a rails helper method for adding a particular class (like .selected
or .active
) to a navigation link if it's url matches the current path.
You can use it in your layout like so:
= nav_link "News", articles_path, {class: "btn btn-small"}
<!-- which creates the following html -->
<a href="/articles" class="btn btn-small selected">News</a>
Nice. I'm using bootstrap, and want to have an icon in my button, so I need to generate the following html:
<a href="/articles" class="btn btn-small selected"><i class="icon-home"> </i> News</a>
I forked the gist and figured out a simple way of doing it. My fork lets the developer pass :inner_html and :inner_class to the helper like so:
= nav_link "News", articles_path, {class: "btn btn-small"}, {inner_html: 'i', inner_class: 'icon-home'}
It works fine, but I don't like my underlying implementation:
def link
if @options[:inner_html]
link_to(@path, html_options) do
content_tag(@options[:inner_html], '', :class => @options[:inner_class]) + " #{@title}"
end
else
link_to(@title, @path, html_options)
end
end
As you can see, I'm passing the new options to content_tag
inside the block of a link_to method. I was hoping I would be able to refactor it in a few ways.
First of all, I'd prefer to be able to do this in my view:
= nav_link "News", articles_path, {class: "btn btn-small"} do
%i.icon-home
I want to give the inner html as a block, and not as attributes of the option hash. Can anyone give me any pointers on how to achieve this?
I thought it would a simple case of telling the nav_link method to accept a block:
def nav_link(title, path, html_options = {}, options = {}, &block)
LinkGenerator.new(request, title, path, html_options, options, &block).to_html
end
class LinkGenerator
include ActionView::Helpers::UrlHelper
include ActionView::Context
def initialize(request, title, path, html_options = {}, options = {}, &block)
@request = request
@title = title
@path = path
@html_options = html_options
@options = options
@block = block
end
def link
if @block.present?
link_to @path, html_options do
@block.call
@title
end
end
end
But this fails to output the icon, and instead inserts a number (4). I don't get it clearly. Anyone got any advice. Where can I go to read more about this sort of thing, as I really want to be able to figure stuff like this out without having to ask on stackoverflow.
I tried your problem and the following worked for me perfectly, in the helper:
def my_link(title, path, &block)
if block_given?
link_to path do
block.call
concat(title)
end
else
link_to title, path
end
end
Usage:
my_link "No title", User.first do
%i.icon-home
The solution in the end was as follows:
# capture the output of the block, early on if block_given?
def nav_link(title, path, html_options = {}, options = {}, &block)
LinkGenerator.new(request, title, path, html_options, options, (capture(&block) if block_given?)).to_html
end
I also had to modify my link method:
def link
if @block.present?
link_to(@path, html_options) do
@block.concat(@title)
end
else
link_to(@title, @path, html_options)
end
end
I've updated my gist. You could probably hack it up to accept more complex blocks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With