I am currently implementing the search functionality in a project and I am struggling displaying it on a dedicated search result page.
Being aware of questions on this topic already but being unable to work out a solution due to utter incompetence, I am asking you for the final pointer :).
The search form spawns on the index page which is entries_path and root_path. I'd like to pass on the parameters to a new page, search_path.
Here are my files:
EntriesController
def search
end
def index
#entries = Entry.all.order('entries.created_at DESC')
#entry = Entry.new # My index page also creates new entries.
if params[:search]
#entries = Entry.search(params[:search]).order("created_at DESC")
else
#entries = Entry.all.order("created_at DESC")
end
Model: entry.rb
def self.search(search)
where("content LIKE ? OR created_at LIKE ?", "%#{search}%", "%#{search}%")
end
routes.rb
Rails.application.routes.draw do
resources :entries
root 'entries#index'
get 'new' => 'entries/new'
get 'show' => 'entries/show'
get 'edit' => 'entries/edit'
get 'search' => 'entries/search'
Finally: the form on index
<%= form_tag(entries_path, :method => "get", class: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search for previous entries..", class: "form-control" %>
<% end %>
When I change the entries_path to search_path, I am getting a "We're sorry, but something went wrong. If you are the application owner check the logs for more information." – therefore, I suspect it is a routing problem. However, I can't seem to figure it out. The log says:
ActionController::RoutingError (uninitialized constant Entries):
Phew, would love to know what's going on here! Thanks a bunch already.
Change your routes.rb
Rails.application.routes.draw do
root 'entries#index'
resources :entries do
collection do
get :search
end
end
end
change your path in search form on index page:
<%= form_tag(search_entries_path, :method => :get, class: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search for previous entries..", class: "form-control" %>
<% end %>
Change your controller's method:
def search
if params[:search]
#entries = Entry.search(params[:search]).order("created_at DESC")
else
#entries = Entry.all.order("created_at DESC")
end
end
create one template for search method under view/entries/search.html.erb
You can here access your #entries object
Points of changes I have made:
1. Changes in routes.rb:
Rails router recognizes URLs and dispatches them to a controller's action. It can also generate paths and URLs, avoiding the need to hardcode strings in your views.
A resource route maps a number of related requests to actions in a single controller. a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. for more information regarding routes
You can add additional routes that apply to the collection or individual members of the collection.
For Eg:
To add a member route, just add a member block into the resource block:
resources :entries do
member do
get 'preview'
end
end
To add a route to the collection:
resources :entries do
collection do
get 'search'
end
end
A member route will require an ID, because it acts on a member. A collection route doesn't because it acts on a collection of objects. for more info about
difference between collection route and member route in ruby on rails?
2. Which method should I use for search GET or POST?
There are numbers of post available regarding GET and POST request on the web as well as SO. GET and POST both have their place, and if you’re a Web developer you should understand the pros and cons of each of them. Or if you’re too lazy to do that, just remember that Search forms should use GET method. Your users will appreciate it. ;)
Let me define them in short description.
GET to fetch a resource(when you don't want to make any change in your DB), POST to create a resource(when you want to make a change/create in your DB), PUT (or PATCH, these is debate on the matter) to update a resource, DELETE to delete one.
For your reference:
When do you use POST and when do you use GET?
I hope this information may helps you. Good Luck :)
You could use html as a search field on Index page.
<form>
<legend>Search</legend>
<div class='col-xs-4'>
<input type='text' class='form-control' value='<%= params[:search] %>' name='keyword' placeholder='Keyword' >
</div>
</form>
Note: This form would hit your index action of entries controller, So at this point no need to create a search methods as you have created
Related
Routes:
get 'home/index' => "home#index"
namespace :lawyers do
get 'all/:division/:district' => "profiles#index", as: :division_district_all
get 'all/:speciality/:sub_speciality' => "profiles#index", as: :speciality_subspeciality_all
end
Home controller #Index view:
<% #districts.each do |district| %>
<%= link_to district.name, lawyers_division_district_all_path(district.division.name.parameterize,district.slug) %>
<% end %>
<% #sub_specialities.each do |sub_speciality| %>
<%= link_to sub_speciality.name,lawyers_speciality_subspeciality_all_path(sub_speciality.speciality.name.parameterize,sub_speciality.name.parameterize)%>
<% end %>
Profile Controller #index:
raise params.inspect
Every time I hit with speciality and sub_speciality but this shows division and district value in params. It conflicts because the pattern is similar. How can I get rid of this ?
You are going to need to separate the destination method on the controller and update the routes.
I would recommend this approach:
namespace :lawyers do
get 'division/:division/:district' => "profiles#division", as: :division_district_all
get 'speciality/:speciality/:sub_speciality' => "profiles#speciality", as: :speciality_subspeciality_all
end
Update: Based on strong requirements, you could use query params all/:division/:district?query_by=divison you would only need one route.
get 'all/:primary/:secondary' => "profiles#index", as: :lawyers_all
And then in the controller, manage the logic with something like
def index
case params[:query_by]
when 'division'
# Division logic here
when 'speciality'
# speciality logic here
else
# Error handling here
end
end
Update 2: As you mentioned on the comments, URL cannot change. Still you would need only one route
get 'all/:primary/:secondary' => "profiles#index", as: :lawyers_all
And check existence on db based on the params, this will impact the performance of your app by creating a lot of db requests and also will create the potential issue of matching with the incorrect classes.
def index
if Division.find_by(name: params[:primary]).present?
# Division logic here
elsif Speciality.find_by(name: params[:primary].present?
# speciality logic here
else
# Error handling here
end
end
I'm viewing 1 product in show.html.erb and there's a link below that says "View other products from this company". This link_to connects to another non-restful action in same controller which retrieves from DB other products of same company as was shown in show.html.erb.
Can link_to pass the :id of the current product in show to action it's rendering? I'm new to rails and please let me know if question is not making sense. I'm not sure if routes need to be defined as well. Thanks.
products_controller.rb
def show
#company_products = Product.by_company
end
show.html.erb
<%= link_to "View other products from this company", company_products_path(:anchor => "#{#company_products}") %>
routes.rb
get '/company_products_' => 'products#company_products'
I finally resolved it by passing the :id of object in show via link_to to a non-restful action.
I'm open to suggestions if entire #company_products in #show can be passed as it is because I'm first finding if there are any other products for that company and if there are, passing an id only in link_to and in controller#company again running a query to get same data of all products to display. so running same query twice is not DRY.
controller#show remains the same as originally posted.
routes.rb
resources :products do
get :company, on: :member
end
show.html.erb
<%= link_to "View other products from #{#company_name}", company_product_path(#product.company_id) %>
controller#company
def company
#products_of_company = Product.where(company_id: params[:id])
end
Now in company.html.erb, the list is just displayed.
You want to do something like this:
#company_products.each do |company|
link_to "View other products from this company", products_path(company)
end
routes:
resources :products
I'm somewhat new to rails. I'm going through making the classic twitter clone right now. I want to have a search bar on my homepage that allows the user to search for a twitter handle, and if the handle exists, it will send the user to the show page for that twitter handle.
I've been following a RailsCast on how to implement a simple search, but instead of doing it on the index like the video, I want to do it on the show action. I've run into some problems though. The form sits on my user index view.
Here is the error:
ActionController::UrlGenerationError in Users#index
Showing c:/Sites/Projects/twitterapp/twitter/app/views/users/index.html.erb where line #2 raised:
No route matches {:action=>"show", :controller=>"users"} missing required keys: [:id]
Here is the form:
<%= form_tag(user_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
Here is my show action:
def show
#user = User.search(params[:search])
end
And here is my search method in my user model:
def self.search(search)
if search
find(:all, conditions:['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
Actually you cannot use the show method as a search result finder. Because according to the rails convention:
For any resource like users, rails scaffold generates index,new, show, create, update, delete methods based on your routes files.
Thus based on the conventional way, show method always asks for an object. Lets say you are using UserContoller show method. It asks for a user object. Which you haven't provide in the form. that's why :id missing error is given.
I would tell you to do some more learning. And for searching create a different method in a different controller and define that controller method to the routes.rb file. This is the best way to do.
If you still want to use the show method, then change the show methods routing from the routes.rb file. You've to manually declare the show action on routes file.
you are using user_path and path need to inform id from present user
you can do this in action :index but I recommend you to create a action to this
view
<%= form_tag(search_users_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
routes.rb
resources :users do
post 'search', :on => :collection
end
users_controller.rb
def search
#user = User.search(params[:search])
end
You should to create a view search.html.erb similar as index.html.erb
As Emu and Breno pointed what causing the problem user_path requires an user id
Solution idea:
Why not just point to users index action? like this:
<%= form_tag(users_path, method: 'get') do %>
<%= text_field_tag(:search, params[:search]) %>
<%= submit_tag("Search", name: nil) %>
<% end %>
users_controller.rb:
def index
if params[:search]
#user = User.search(params[:search])
end
end
and you can use ajax remote: true to handle the returned user object
Found your question via Google, but the responses and suggestions didn't work for me. Found another solution that did, so seems worth posting here.
"Search and Filter Rails Models Without Bloating Your Controller":
http://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/
I'm trying to implement the datagrid gem in Rails 4 but am not sure how to include a link in the Grid class.
I currently have for the UsersGrid class:
class UsersGrid
include Datagrid
scope do
User.order("users.created_at desc")
end
column(:avatar) do |user|
if user.avatar?
link_to ActionController::Base.helpers.image_tag(user.avatar.url, alt: "Profile"), user_path(user)
else
link_to ActionController::Base.helpers.image_tag("profile.gif", alt: "Profile"), user_path(user)
end
end
end
This generates the following error message referring to the link_to line :
undefined method 'user_path' for #<ActionView::Base:0x007f821d3115b8>
How should I adjust the code to make the link work?
Additional information:
View page:
<%= datagrid_form_for #grid, :method => :get, :url => users_path %>
<%= will_paginate(#grid.assets) %>
<%= datagrid_table(#grid) %>
<%= will_paginate(#grid.assets) %>
Controller method:
def index
#grid = UsersGrid.new(params[:users_grid]) do |scope|
scope.where(admin: false).page(params[:page]).per_page(30)
end
#grid.assets
end
I found the solution: I had to add :html => true to column(:avatar, :html => true). This way html code such as link_to work and I also no longer needed ActionController::Base.helpers to get access to the image_tage method.
It sounds like you don't have a route configured for a user resource in routes.rb.
To verify and see what path helpers are available, go to command line, navigate to the project directory, and type in rake routes. If properly configured you should see something like:
user GET /users/:id/(.:format) users#show
On the far left of the example above is the "Name" which is used to generate the path helper. So in the example above the name is "user" and Rails will automatically generate the helper user_path which accepts an argument that is a user's id. So user_path(1) is a helper for /users/1. If you pass in a User object (like you were in your example) it will just get the id from the User in the background e.g.) user_path(current_user) will find the id of current_user and return /users/1.
Read more about rake routes here.
Anyways, if user is missing from your routes.rb file you could add something like this to routes.rb:
get '/users/:id', :to => 'users#show', :as => :user
Keep in mind you likely already have something for the users resource, so you might be able to make an easier/cleaner change depending on how you have configured the file.
I am looking to use link_to to call a method in my controller. However, for some odd reason the route looks for the show method.
In my view:
<% #beverages.each do |beverage| %>
..
<%= link_to 'Archive', beverages_archive_path(:id => beverage.id) %>
..
<% end %>
In my config/routes.rb
match 'beverages/archive' => 'beverages#archive'
In my beverages_controller.rb
def archive
beverage = Beverage.find(params[:id])
respond_to do |format|
# format.html # show.html.erb
format.json { render json: beverage }
end
# beverage.update_attribute('archive', true)
end
When I click on the archive link in the view, the URL does change to: http://localhost:3000/beverages/archive?id=11, however I get the following error.
The error I get:
ActiveRecord::RecordNotFound (Couldn't find Beverage with id=archive):
app/controllers/beverages_controller.rb:46:in `show'
Any idea on what I am doing wrong? Your help is much appreciated!
PS. I also looked at Rails 3 link_to delete destory method calls show method?
but nothing seemed to work.
Have you tried this in your routes?
match 'beverages/:id/archive' => 'beverages#archive', as: :beverages_archive
This will create the beverages_archive_path method for you. Also, as you are looking for a specific beverage, use :id inside the route so that it knows where to take the id parameter from.
Apart from that, you can always tell a link specifically which controller and action to link to by doing:
link_to "Label", :controller => :my_controller, :action => :index
Taken from here: Ruby on rails 3 link_to controller and action
Use the other notation (not match) instead.
resources :beverages do
collection do
get :archive
end
end
Try this one out and let me know if something went wrong.
There's not quite enough information here to know why beverages_archive_path works in your app -- one problem is that your routes file does not define a name for your beverages#archive route. What you want is something like:
match 'beverages/archive' => 'beverages#archive', :as => :beverages_archive
or better yet use resourceful routing conventions like so:
resources :beverages do
collection do
get :archive
end
end
What's happening is that you have a beverages#show route that matches /beverages/:id, and this is what /beverages/archive matches (with :id => 'archive').