I'm using this code to highlight currently active menu tab with Twitter Bootstrap:
def nav_link_to(link_text, link_path, options = nil)
class_name = current_page?(link_path) ? 'active' : ''
content_tag(:li, :class => class_name) do
link_to link_text, link_path, options
end
end
This of course makes the link active, only if the given link IS the current page.
How can I change this function, such that it would return 'active' for any links below current controller?
In other words, for all actions for Posts controller, the links would be active?
You can use controller_name to get the name of the current controller.
This could be solved with:
Rails.application.routes.recognize_path(link_path)[:controller]
params[:controller] returns controller name with its namespace.
If you want to get controller name only you should use controller.controller_name.
class Admin::Posts; end
# from the view
params[:controller]
=> 'admin/posts'
controller.controller_name
=> 'posts'
Not sure if I read this correctly, but you shouldn't use a get param to see the current controller.
Have you tried using request.path ? In other words, check if any of the links below include 'request.path' in their path...
Related
I'm trying to add a class to a link's container when the link is active.
To be more precise : I want a class applied on a sidebar link container if the user is navigating through a group of pages, usually related to a model. For example, for the user management part of the site, I want the sidebar link to look active on the user index, the user creation form and the user show page.
I have this helper method :
def active_class_if_url(urls)
urls.include?(request.path) ? 'sidebar-active-item' : ''
end
And I use it in my views like so :
<div class="<%= active_class_if_url([users_path, new_user_path]) %>">
It works for index pages and creation forms, but I can't make it work when the path helper has a parameter, for example a show page like user_path(#user).
How can I make my helper understand these paths ? Is that even the right method to do this ?
Update 1
I tried the current_page method, as suggested by #markets, still not working. Tried this :
def active_class_if_controller(controller)
current_page?(controller: controller) ? 'sidebar-active-item' : ''
end
hoping this would do the trick :
<div class="<%= active_class_if_controller('users') %>">
But nothing changed, it gets only the index action. I'm currently trying something along this answer : https://stackoverflow.com/a/21536442/3219759
Update 2
Solved via the previous answer :
def active_class_if_controller(controller)
params[:controller] == controller ? 'sidebar-active-item' : ''
end
Does the trick.
You can use the provided helper by Rails current_page? (docs: https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-current_page-3F). You can even check for specific params if you need it.
Examples:
If you in the page: http://www.example.com/shop/checkout?order=desc&page=1
current_page?(action: 'process')
# => false
current_page?(action: 'checkout')
# => true
current_page?(controller: 'library', action: 'checkout')
# => false
current_page?(controller: 'shop', action: 'checkout')
# => true
current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
# => false
current_page?('http://www.example.com/shop/checkout')
# => true
current_page?('http://www.example.com/shop/checkout', check_parameters: true)
# => false
current_page?('/shop/checkout')
# => true
current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
# => true
Alternatively, you can also write your own simple helper, using the controller_name (and/or action_name) macro:
def active_class_if_controller(controller)
'sidebar-active-item' if controller_name == controller
end
I’m using Rails 4.2.3. I want to have a tabbed menu on my admin login page, so I added the following to my app/controllers/admin_controller.rb file …
class AdminController < ApplicationController
before_filter :must_be_admin, only: :index
def index
#page_id = "index"
end
def menu_builder(page_id)
tabs = ['Current Hacks','Add New Hack','Approve Hacks']
content = ""
tabs.each do |tab|
content << if page_id == tab
content_tag('li', content_tag('a', tab, :href => nil ), :class => 'current') + " "
else
content_tag('li', content_tag('a', tab, :href => "/#{tab}" )) + " "
end
end
content
end
Here is what I have in my app/views/admin/index.html.erb file …
<h1>Admin#index</h1>
<ul>
<%= menu_builder(#page_id) %>
</ul>
However, when I visit my /admin/index page, I get this error
undefined method `menu_builder' for #<#<Class:0x007f861bfe9900>:0x007f861bff36f8>
Where am I going wrong?
for a rails method to be available inside your views,you must define it in helpers and not in controllers.
for example
admin_helper.rb
def menu_builder(page_id)
###definition
end
so now ,this method can be called within any views inside admins/ coming from admins_controller.rb
but if you want it to be available to all views,then you must add it in application_helper.rb
You cannot use controller actions right into your views (they are called from routes via HTTP requests).
You need the menu_builder method to be defined in an AdminHelper module, and then call helper AdminHelper in your AdminController.
See http://api.rubyonrails.org/classes/ActionController/Helpers.html for more information.
Hey it is looking this method in its helper . define that method in helper and then call it the view. You cannot call controller methods from a view..
Please add menu_builder method to AdminHelper(app/helpers/admin_helper.rb). Otherwise, add menu_builder method to some other helper and include that helper in your AdminController. It is preferable to have menu_builder method in AdminHelper.
Methods in a controller generally meant for "actions", typically have to be defined and consumed through Routes.
Just move the method from controller to any helper maybe applicaiton_helper then you are able to create view a you need
I have a User model wich has a controller index and an '.all' scope
ex: User.all
Is there a decent way to switch to a different scope when you clic on a view link?
I want to display all user when a user clic an 'all' link or a scoped version when he clic on an other link.
Do i have to use a gem like has_scope?
Thanks
You can use scoped method, for example:
#users = User.scoped
#users = #users.your_scope if params[:your_scope_param]
all you have to do is to bind appropriate param to your "scoping" link, it should be like:
<%= link_to 'Scoped users', users_path(your_scope_param: true) %>
I have a helper method to help to determine whether or not a navigation menu item should be active/inactive.
def events_nav_link
nav_item = 'Events'
if current_page?(events_path) # additional conditions here
# do nothing
else
nav_item = link_to nav_item, events_path
end
content_tag(:li, nav_item + vertical_divider, :class => 'first')
end
I want this link to be inactive not only for the events_path page, but for other pages as well. Problem is that I don't know what param to pass the current_page?(path) method based on this route:
map.list_events '/events/:league/:year/:month/:day', :controller => 'events', :action => 'list'
This route generates a path such as /pow or /chow/2011/09/25. Leagues can be pow, chow or wow.
I like unobtrusive JS approach with add/remove classes and unwrap() deactivated links, but it requries specific rules.
The Rails way is to use link_to_unless_current built-in helper to highlight and unlink on current page href.
You're looking for named routes. In your routes.rb file, add
:as => :foo
after the route. Then use
if current_page(events_path) || current_page(foo_path)
in your condition.
This is what I do:
application_helper.rb:
def already_here?(this_controller,this_action)
controller_name == this_controller && action_name == this_action ? true : false
end
in a view:
<%= already_here?("users","index") ? "Manage Users" : link_to("Manage Users", users_path, :title => "Manage Users.") %>
Of course, you can abstract it further by passing the title of the link and path to the helper if you want, but that's a bit of a hassle (to me).
UPDATE: never mind, see mikhailov's answer- much cleaner (why reinvent the wheel?) :)
I need help trying to create a link that submits an edit form.
Let's say I have a list of objects
Object - Color - Own?
Ball - Red - false - [button]
Hat - Blue - true - [button]
Shoe - Green - false - [button]
When I click on the [button] I want to set "Own?" to True.
Routes
resources :toys
Controller
def edit
#toy = Toy.find(params[:id])
end
def update
#toy = Toy.find(params[:id])
if #Toy.update_attributes(params[:toy])
flash[:notice] = "Toy Updated"
redirect_to #toy
else
render 'edit'
end
end
View
<h2>Toys</h2>
<% if #toys %>
<% #toys.each do |toy| %>
<%= toy.name %> - <%= link_to 'Set Own', edit_toy_path(:id=>toy.id, :owned=>'true')%>
<br/>
<% end %>
<% else %>
None
<% end %>
This is all about how you setup your controller actions. I'm not totally sure I understand how you want to use yours, but I have a similar case that I'll show you which I think you should be able to adapt to your situation.
In my case, I have a menu button that sets a value in the session to either keep a menu panel open or closed across any views a user looks at.
First, you need a controller action that is going to do the work you're interested in. I created a "SharedController" which handles application-wide things that don't belong to any particular view or other controller.
class SharedController < ApplicationController
# Used by AJAX links to set various settings in shared views
def edit
session[:admin_menu] = params[:admin_menu].to_sym if params[:admin_menu]
session[:advanced_search] = params[:advanced_search].to_sym if params[:advanced_search]
render :nothing => true
end
end
This controller action can set one of two values in the session, either: "admin_menu" (boolean) or "advanced_search" (boolean). Then certain views ask whether the session value for admin_menu or advanced_search is true, and if so they show the view.
You could use the same logic. Something like:
def edit
object= Object.find(params[:object_id])
object.own = params[:own]
object.save
end
To trigger this controller action with a link you need to have a route that accepts GET requests. edit is a logical choice.
resource :shared, :only => [:edit], :controller => 'shared'
Note: I think SharedController makes more sense than SharedsController, and edit_shared_path makes more sense than edit_shareds_path, so I had to specify :controller => 'shared' in my routes.rb.
Then you just need a link to a url with params. To add params onto a path you just add them to the path helper, like so:
edit_shared_path(:key => 'value')
You can retrieve these params in your controller via:
params[:key]
Make this a link like so:
link_to 'Set Own to True for This Object', edit_shared_path(:object_id=>object.id, :own=>'true')
NOTE: It's best to do this via AJAX, so be sure to set :remote=>true. If you don't use AJAX then you need to specify a redirect in your controller for what page should be loaded after this link is triggered.
In the case of my admin menu preference link, I need a link with two possible states. I generate these using a helper:
# Shows Admin Menu Button
def admin_toggle_button
if session[:admin_menu] == :on
link_to( 'Admin Tools', edit_shared_path(:admin_menu => :off), :remote=>true, :class => 'selected', :id => 'admin_toggle_button', :title => 'Hide Admin Menu' )
else
link_to( 'Admin Tools', edit_shared_path(:admin_menu => :on), :remote=>true, :id => 'admin_toggle_button', :title => 'Show Admin Menu' )
end
end
In a view I just call this using admin_toggle_button. You can do something similar if you like, but it's optional.
I hope that gets you on the right track, let me know if you have any questions.
EDIT: Based on your comment:
Links issue GET requests, which mean you're going to the EDIT action. See: http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
A further issue, you have resources :toys instead of resource :shared (which I used for this purpose). This means your link helper is already expecting a specific toy to edit, rather than handling a singular resource. See: http://guides.rubyonrails.org/routing.html#singular-resources
Your link would work if you changed it to be:
link_to 'Set Own', edit_toy_path(#toy, :owned=>'true'), :remote => true
... and set your edit action in your controller to the following:
def edit
#toy = Toy.find(params[:id])
#toy.owned = params[:owned]
if #toy.save!
head :ok
else
head :internal_server_error
end
end
See: http://guides.rubyonrails.org/layouts_and_rendering.html#using-head-to-build-header-only-responses
Now, be aware, you really should only do this with AJAX links, and you should normally not do it with your "real" controller. The reason is, now this is the only action that can be processed by EDIT, so your normal toys#edit view would no longer work.
You can get around this by create a new action and a new route, for instance:
resources :toys do
member do
get 'set_ownership'
end
end
Then simply take the same method above and call it set_ownership instead of edit. IE:
class ToysController < ApplicationController
...
def set_ownership
...
end
end
Hope that all makes sense.
The edit_toy_path method that your link_to method is calling is going to the edit action inside your controller. It's not going to the update method that I'm guessing you want.
Your link_to will need to change to something like:
<%= link_to 'Set Own', toy_path(:id=>toy.id, :owned=>'true'), :method => :put %>
But I question this particular approach. I don't think the variable will update correctly in the update action because it is not namespaced to the proper params[:toy] object that update_attributes is expecting. And in my quick and dirty tests I couldn't get it to namespace properly.
When I have a situation like the one that you are describing I usually setup another action, like toggle_ownership and I call that from my link_to with a :remote => true option. Then the controller toggles the attributes as desired.
Thus, my routes looks something like:
resources :toys do
member do
put :toggle_ownership
end
end
And my view looks like
<%= link_to 'Set Own', toggle_ownership_toy_path(toy.id), :method => :put %>
The controller sets the variable and renders back a toggle_ownership.js.erb file that updates the appropriate section of the page.
Hope that helps!