Add an 'active' class to all active links in rails? - css

Basically, I have a lot of code that looks like this:
link_to t('.profile'), business_path(#business), class: '#{'active' if current_page? business_path(#business)}'
which isn't very DRY.
I was wondering if anyone knows a good way to modify the link_to helper itself to automatically add an 'active' class to all links to the current page.
If it helps, I'm open to using HAML or SLIM.

I wrote simple helper method using build in view helper current_page? when you can specify custom class name in html_options hash.
def active_link_to(name = nil, options = nil, html_options = nil, &block)
active_class = html_options[:active] || "active"
html_options.delete(:active)
html_options[:class] = "#{html_options[:class]} #{active_class}" if current_page?(options)
link_to(name, options, html_options, &block)
end
Examples (when you are on root_path route):
<%= active_link_to "Main", root_path %>
# Main
<%= active_link_to "Main", root_path, class: "bordered" %>
# Main
<%= active_link_to "Main", root_path, class: "bordered", active: "disabled" %>
# Main

I faced same requirement and here is my solution.
Create a method within ApplicationHelper
def active_class(link_path)
current_page?(link_path) ? "active" : ""
end
And inside your view:
<li class="<%= active_class('/') %>">
<%= link_to 'HOME', root_path %>
</li>

It's a solved problem, just use active_link_to gem. Your example simplifies to this:
= active_link_to t('.profile'), business_path(#business)

This is a good case for writing your own helper that wraps the link_to. In your application_helper.rb you can write a method active_link_to that takes the same params as link_to + current_page, and then just calls link_to like you are doing above.

as per rails 6.1 now we have helper for html class name
the helper example
class_names("foo", "bar")
# => "foo bar"
class_names({ foo: true, bar: false })
# => "foo"
class_names(nil, false, 123, "", "foo", { bar: true })
# => "123 foo bar"
you could use it like this
<%= link_to 'Home', root_path, class: class_names('nav-link', { active: current_page?(root_path) }) %>
it will produce html like this
<a class="nav-link active" href="/">Home</a>
the doc is here

Here's the helper I use. I add an optional "match_text" parameter for added flexibility (for instance, if I want to mark a link as active when the actual request path is a child page of the link's destination.)
def link_to_active(text, destination, options = {})
match_text = options.delete(:match_text)
classes = options[:class].present? ? options[:class].split(" ") : []
classes << "active" if request.fullpath.downcase == destination.downcase || (match_text && request.fullpath.downcase.include?(match_text.downcase))
options = options.except(:class)
options.merge!(:class => classes.join(" ")) unless classes.empty?
link_to(text, destination, options)
end

Great solution if you're using rails 6.1+.
Also works great when passing a block.
def active_link_to(text = nil, path = nil, **opts, &block)
link = block_given? ? text : path
opts[:class] = class_names(opts[:class], { active: current_page?(link) })
if block_given?
link_to link, opts, &block
else
link_to text, path, opts
end
end

I did the same that #egyamado. I needed to use AwesomeIcons too, so:
A helper:
def active_class?(link_path)
'active' if current_page?(link_path)
end
And it was my view:
<%= link_to my_controller_page_path,
:title => "My Controller Page",
:class => "other_name_class #{active_class?(my_controller_page_path)}" do %>
<i class="fa fa-fighter-jet"></i> My Controller Page
<%end%>
In another kind of Link, for example inside a Li.
#In this case I put a extra validation in root_path
<li class="nav-class <%=active_class?(my_controller_page_path)%> <%='active' if current_page?(root_path) %>">
<%= link_to my_controller_page_path,
:title => "Page 1",
:class => "other_name_class" do %>
Page 1
<%end%>
</li>
<li class="nav-class <%=active_class?(my_controller_page_2_path)%>">
<%= link_to my_controller_page_2_path,
:title => "Page 2",
:class => "other_name_class" do %>
Page 2
<%end%>
</li>
It worked for me.

This is my custom method to handle this issue.
def active_link_to(name = nil, options = nil, html_options = nil, &block)
if current_page?(options)
active_class = html_options[:active_class] ? html_options[:active_class] : 'has-text-danger'
html_options[:class] << "#{html_options[:class]} #{active_class}"
end
link_to(name, options, html_options, &block)
end
html_options[:active_class] is a custom hash.
Now I can dynamically change styles of my active link.
<%= active_link_to "Menu", root_path, class: 'has-text-dark', active_class: 'has-text-danger' %>

Here's a basic example from which you can expand:
module ApplicationHelper
def active_link_to(text, path, **opts, &block)
css_class = opts[:class]
case opts[:endpoint]
when String then css_class.concat(' active') if current_page? opts[:endpoint]
when Array then css_class.concat(' active') if opts[:endpoint].include? request.path
end
if block_given?
link_to path, class: css_class, &block
else
link_to text, path, class: css_class
end
end
end
# app/views/layout/_navbar.html.erb
# Using multiple endpoints
<%= active_link_to 'Home', root_path, class: 'some-class', endpoint: ['', '/', '/home'] %>
# Using a single endpoint
<%= active_link_to 'Admin', admin_path, class: 'some-class', endpoint: '/admin' %>
# Using a block
<%= active_link_to admin_path, class: 'some-class', endpoint: '/admin' do %>
<h1>Administration</h1>
<% end %>

nav.slim:
li class="nav-item"
= active_link_to 'Users', users_path, "nav-link"
application_helper.rb:
def active_link_to(title, path, class_css, options = {})
if current_page?(path)
class_css = "#{class_css} active"
options.merge!('aria-current': "page")
end
link_to title, path, class: class_css, **options
end

Use link_to_unless_current and then give it the look of an active link in CSS.

Related

How do I add class tag to this rails link_to tag in slim?

I wanted to add class name 'button' to this following tag.
= link_to 'Upload data', new_raw_datum_path if logged_in?
So I tried this,
= link_to 'Upload data', new_raw_datum_path if logged_in?, class: 'button'
but it gave me back this error
syntax error, unexpected ',', expecting ')'
...w_raw_datum_path if logged_in?, class: 'button'), true, "\n ...
...
My way of adding a class name worked fine in other pages but not this page.
(This is a link in my navigation)
How could I add them in slim???
Thank you for your time.
^
It should be:
= link_to 'Upload data', new_raw_datum_path, class: 'button' if logged_in?
You can use link_to_if of rails actionview:
= link_to_if(logged_in?, "Login", new_raw_datum_path, class: 'button') do
link_to 'Upload data', new_raw_datum_path
end

Rails collection_select + bootstrap dropdown

<div class="dropdown">
...
<%= ff.collection_select :vendor_id, #vendors, :id, :vendor, :include_blank => true, class: "dropdown-menu" %>
...
</div>
I have a form and then I'm using fields_for as shown above to collect input. But I'm unable to see the stylized bootstrap dropdown as illustrated here. What changes should I be making to see the bootstrap stylized dropdown?
from
<%= ff.collection_select :vendor_id, #vendors, :id, :vendor, :include_blank => true, class: "dropdown-menu" %>
to
<%= ff.collection_select :vendor_id, #vendors, :id, :vendor, {:include_blank => true}, {class: "dropdown-menu"} %>
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

Ruby on Rails: Required input field CSS not showing

I have a form and I'm setting a field to be required before submitting, however nothing is showing up when I hit the Search button. What do I need to do to style the form?
<%= form_tag search_path, :method => :get, class: "form-search-home" do %>
<%= text_field_tag :q, :class => "term form-control" %>
<%= text_field_tag :loc, :class => "loc form-control", :required => true %>
<%= button_tag :type => :submit, :class => "btn" do %>Search<% end %>
<% end %>
Thanks!
nothing is showing up when I hit the Search button
The problem here is likely a Rails / HTML issue than CSS (as mentioned in your question)
Syntax
As pointed out in the comments, you have a series of problems with your code syntax, specifically with submit_tag & text_field_tag:
<%= form_tag search_path, method: :get, class: "form-search-home" do %>
<%= text_field_tag :q, nil, class: "term form-control" %>
<%= text_field_tag :loc, nil, class: "loc form-control", required: true %>
<%= submit_tag "Search", class: "btn" %>
<% end %>
This should fix any of the syntax issues you have on your form, allowing it to submit. The reason why it doesn't at the moment is likely down to the syntax being incorrect. If you use the above code, it should render the form correctly, allowing you to submit it as required!
--
CSS
CSS is cascading style sheets - meaning they're only meant to style your page. They can't fix any syntax, backend or HTML rendering issues - only how the HTML appears in the browser
If you've still got trouble with your CSS, you'll be best styling the form with the inputs inheriting from the main class styling:
#app/assets/stylesheets/application.css
form {
/* form code */
}
form input.required {
/* required form element styling */
}
Does your form code generate a valid HTML?
As far as I see from documentation, text_field_tag method has three arguments:
text_field_tag(name, value = nil, options = {})
Your example ommits the second argument (value), so may be that is the case. Wonder if this could help:
<%= text_field_tag :loc, nil, :class => "loc form-control", :required => true %>

Why is rails adding the CSS class of a button as an URL parameter?

I'm trying to style links with Rails' link_to helper and Bootstrap CSS styles.
Here's one of my links:
<%= link_to 'Home', controller: 'welcome', :class => "btn btn-primary" %>
The style gets applied to the link, however the url that link_to generates has the css class as a parameter in it, suggesting I'm doing something wrong, like this:
/welcome/index?class=btn+btn-primary
Why is this happening and is there a better way to apply css styles to links?
Try this
<%= link_to "Home", controller: "welcome", action: "your_action", :class => "btn btn-primary" %>
OR
<%= link_to "Home", welcome_path, :class => "btn btn-primary" %>
The class should be in html_options encapsulated between curly braces:
link_to(body, url, html_options = {})
Try
<%= link_to 'Home', controller: 'welcome', {:class => "btn btn-primary"} %>

Conditional Display Rails

I'm trying to display a delete button only if the current user is the "host" but the button is hidden even if the user id for the host matches the user id for the guest
<%= link_to "X" , "/songs?name=#{s.name}&party_profile_id=#{s.party_profile_id}&commit=X", :remote => true, :class => "btn btn-inverse btn-small refreshbutton", :style => "display:none" unless #current_user.id == #party_profile.host %>
Am I using the wrong syntax? Is there a better way to conditionally display items?
You could switch to only render it, rather than toggling the css class (unless your application would need to toggle it client side for some reason).
<% if #current_user.id == #party_profile.host %>
<%= link_to "X" , "/songs?name=#{s.name}&party_profile_id=#{s.party_profile_id}&commit=X", :remote => true %>
<% end %>
Your condition would apply to the link itself, not the style attribute.
Do this instead:
<%= link_to "X" , "/songs?name=#{s.name}&party_profile_id=#{s.party_profile_id}&commit=X", :remote => true, :class => "btn btn-inverse btn-small refreshbutton", :style => "#{'display:none;' unless #current_user.id == #party_profile.host}" %>
shouldn't it be
if #current_user.id == #party_profile.host ?
Edit:
you don't need to set the style to display none. The if statement will handle whether it is rendered in the view.

Resources