4

I want to render a link only if the condition is met with the following link_to_if:

<%= link_to_if policy(@user.add?), new_entry_path(), class: 'btn' do %>
  <%= glyphicon("plus") %>
<% end %>

The block with the glypicon("plus") helper method should also only be called if the condition of the link_to_if is met. With the code above the block is always called, no matter whether the condition is true or false.

How do I create the link and its inner content only if the condition returns true?

mabu
  • 286
  • 8
  • 26
  • See the thread on the Rails Github here for ramblings on this default behaviour and my personal thoughts on why I think it's incorrect: https://github.com/rails/rails/issues/14830 – Joshua Pinter May 20 '19 at 20:45

4 Answers4

5

Just use a simple if and a link_to:

<% if policy(@user.add?) %>
  <%= link_to new_entry_path, class: 'btn' do %>
    <%= glyphicon('plus') %>
  <% end %>
<% end %>

Or you might want to consider a view helper method like this:

def link_to_add(url)
  link_to(glyphicon('plus'), url, class: 'btn')
end

Which can be used in to view like:

<%= link_to_add(new_entry_path) if policy(@user.add?) %>

Or more generic:

def icon_link(icon, url)
  link_to(glyphicon(icon), url, class: 'btn')
end

Which can be used in to view like:

<%= icon_link('plus', new_entry_path) if policy(@user.add?) %>
Anthony
  • 15,435
  • 4
  • 39
  • 69
spickermann
  • 100,941
  • 9
  • 101
  • 131
4

The block for this method is the "default" behavior, it's the equivalent of the else in an if statement. Here's the source:

  def link_to_if(condition, name, options = {}, html_options = {}, &block)
    if condition
      link_to(name, options, html_options)
    else
      if block_given?
        block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
      else
        ERB::Util.html_escape(name)
      end
    end
  end
Anthony
  • 15,435
  • 4
  • 39
  • 69
  • Well, I thought too far... Do you have any recommendations how I can achieve my goal with as less code as possible? Thanks anyways! – mabu Feb 19 '18 at 14:40
1

As pointed out by @Anthony if the condition is false it will use the block to determine output similar to an else statement.

If no block is given the "name" value will be rendered without an anchor tag. To avoid this you could pass an empty block like so

<%= link_to_if(policy(@user.add?),glyphicon('plus'),new_entry_path,class: 'btn') {} %>

Since the deference is to the block and the block returns nil nothing will be rendered but obviously the interpretation of what is happening here lacks greatly from that of the answer provided by @spickermann.

engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • Thanks to your contribution I now understand how `link_to_if` really works. That it renders the name if the condition is false, and no block is provided, was the behavior I didn't understand. – mabu Feb 20 '18 at 09:28
1

If you would want to show the block anyways, but only add the link if a certain condition is met, you may capture the block altogether:

<% block_content = capture do %>
  <%# makes sense if this is much more complex %>
  <%= glyphicon('plus') %>
<% end %>

<% if policy(@user.add?) %>
  <%= link_to block_content, new_entry_path, class: 'btn' %>
<% else %>
  <%= block_content %>
<% end %>
estani
  • 24,254
  • 2
  • 93
  • 76