💡

An improvement on conditional links in Rails

Ruby Ruby on Rails

Rails' link_to_if helper is limited, yet the situations where you need conditional links are not uncommon. Improve your conditionally anchored elements by implementing this neat little helper!

In Rails, the UrlHelper module from ActionView let's yo use the method link_to_if(condition) to display links conditionally.

It's presented like this :

<%= link_to_if(condition, name, options = {}, html_options = {}, &block) public %>

If the condition is false, it will only render name, if true it will render a link that looks like this :

<a href="options">name</a>

You can make it render nothing in the falsey case by adding {} before the closing tag

<%= link_to_if(condition, name, options){} %>

But I found there are 3 limitations with this method :

  1. The false expression cannot return anything except the name you provide. So if you want to display anything else than text (for example a div) in the falsey condition, you have to pass that in a variable before, and sanitize it later.
  2. The false expression will ignore anything you pass in html_options, including classes.
  3. When written as a code block that wraps an element, if condition is true, name will replace anything that is in the block, so you'd still have to use a variable to 'preserve' the inner part.

In other words, the main intention behind this method is mostly to render a string that can conditionally have an anchor or not.

But let's suppose you have an other element, say an image, that you want to be clickable (to reach the user's profile) ONLY if the current_user is an admin? Wouldn' it be nice if we could something like this :

<%= link_to_if(current_user&.is_admin?, user) do %>
  <%= image_tag user.avatar %>
<% end %>

Well, we can write a custom helper to achieve that. We can name it link_to_cond. I've seen it written differently across codebases but I set it this way :

# application_helper.rb
def link_to_cond(cond, *args, &block)
  if cond
    link_to(*args, &block)
  else
    capture(&block)
  end
end

That's it! You can now conveniently do this :

<%= link_to_cond(conditon, path) do %>
  <span>This may or may not get wrapped in a link!</span>
<% end %>

→ Let's get in touch

Got questions or feedback about this article? Interested in discussing your project? I'm all ears and always open to new opportunities. Shoot me an email and tell me what you have in mind.