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
Related
I need to capture a field added by a user in a form_for, inside the product show page.
My product.rb model as follows:
belongs_to :user
has_many :complaints
My complaint.rb model as follows:
belongs_to :product
belongs_to :user
My user.rb model as follows:
has_many :products
My product controller is a basic controller with all the new, create, edit, update actions and all the routes are good.
User looks at the product show page like this, and it's all good
http://localhost:3000/products/1
My goal is to create a complaint from the product show page, when user views the specific product. So I have created a complaints_controller.rb to capture all the details of the product, and create a complaint. I have an issue with capturing the complaint_number which is a field inside the complaints table.
Here is my form inside the product show page
<%= form_for([#product, #product.complaints.new]) do |f| %>
<%= f.number_field :complaint_number, placeholder: "Enter complaint number you were given" %>
<%= f.submit 'Complaint' %>
<% end %>
Here is my complaints_controller.rb
Goal is to capture the complaint_number fields and run the make_complaint method to create a complaint and populate rest of the fields in the newly created row of the complains table.
class ComplaintsController < ApplicationController
before_action :authenticate_user!
def create
# Will Get product_id from the action in the form in product show page.
product = Product.find(params[:product_id])
# This complaint_number does not seem to work
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
# Now I want to run a make_complaint method and pass the product and the complaint number. This fails, I can't capture the complaint_number in the form from user input.
make_complaint(product, complaint_number)
redirect_to request.referrer
end
private
def make_complaint(product, complaint_number)
complaint = product.complaints.new
complaint.title = product.title
complaint.owner_name = product.user.name
complaint.owner_id = product.user.id
# Note: complaint_number and current_complaint are a fields in the Orders table
# Note:
complaint.current_complaint = complaint_number
if complaint.save
flash[:notice] = "Your complaint has been sent!"
else
flash[:alert] = complaint.errors.full_messages
end
end
end
For routes I have added resources :complaint, only: [:create] inside the resources of products to get products/:id/complaints
My routes.rb is like this
Rails.application.routes.draw do
get 'products/new'
get 'products/create'
get 'products/edit'
get 'products/update'
get 'products/show'
root 'pages#home'
get '/users/:id', to: 'users#show'
post '/users/edit', to: 'users#update'
resources :products do
member do
delete :remove_image
post :upload_image
end
resources :complaint, only: [:create]
end
devise_for :users, path: '', path_names: { sign_in: 'login', sign_up: 'register', sign_out: 'logout', edit: 'profile' }
Your form has complaint_quantity:
<%= form_for([#product, #product.complaints.new]) do |f| %>
<%= f.number_field :complaint_quantity, placeholder: "Enter complaint number you were given" %>
<%= f.submit 'Complaint' %>
<% end %>
Your controller has complaint_number:
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
If you check your params from the server log, I bet you'll see the value you are looking for is coming across as complaint_quantity and not complaint_number.
UPDATE
With the form misspelling corrected, the error persists, so let's check into more areas:
complaint_number = product.complaints.find_by(complaint_number: params[:complaint_number])
So, break that down:
1. What does params actually include?
Is :complaint_number being submitted from the form?
If not, the form still has an error somewhere.
2. Does product.complaints actually include a complaint that could be matched by complaint_number?
I don't know your data structure well enough to tell, but it looks to me like you might actually want to do:
Complaint.find_by(complaint_number: params[:complaint_number])
instead of:
products.complaints.find_by(complaint_number: params[:complaint_number])
UPDATE #2
You know the problem is with your params.
I'm confident you aren't accessing your params correctly since you are using a nested form:
form_for([#product, #product.complaints.new])
Should mean your params are structured like { product: { complaint: { complaint_number: 1234 }}}
So params[: complaint_number] is nil because it should really be something like params[:product][:complaint][:complaint_number]
Please look at your server log in your terminal right after you submit the form to see the structure of your params. Or insert a debugger in the controller action and see what params returns.
ALSO, Instead of accessing params directly, you should whitelist params as a private method in your controller.
Something along these lines:
private
def product_complaint_params
params.require(:product).permit(:id, complaint_params: [ :complaint_number ])
end
See this: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html
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 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
I want to pass the current project's id to the tickets controller (creating a ticket for the project), which I try to do below. However, the way I do it below gives me the following link:
tickets/new?project_id=8
...when I just want it to be this way:
tickets/new
...even though I want the project_id to be accessible in the controller.
How can I do this? Just to clearify: I don't want the project_id to be a part of the URL, I just want to pass it (in some way) as a parameter to the controller.
from view:
<h1><%= #project.title %></h1> <-- the project's attributes is reachable here
<%= link_to "Create ticket", new_ticket_path(:project_id => #project.id), :class => "btn edit_button" %>
tickets controller:
1. class TicketsController < ApplicationController
2. def new
3. #ticket = Ticket.new
4. #id = params[:project_id]
5.
6. #project = Project.find(#id)
7. end
8. end
The route that link_to points to looks like the following:
new_ticket GET /tickets/new(.:format) tickets#new
If the tickets belong to the project, you might want to consider nesting resources.
Right now, your routes probably look like this:
resources :projects
resources :tickets
This generates routes like /projects/new and /tickets/new. What you can do instead is this:
resources :projects do
resources :tickets
end
This will give you routes like /projects/8/tickets/new. You link to a new ticket by doing new_project_ticket_path(#project). The actual form of the route generate is: /projects/:project_id/tickets/:id. So params[:project_id] will give you the id of the project.
I am struggling to pass an id successfully into my URL for the nested resource I have set up called Jobs.
The error I am getting when I try to pass the #job object into my link is as follows:
No route matches {:action=>"edit", :controller=>"jobs", :user_id=>1, :id=>nil}
Which clearly shows it can't find the id correctly and so is finding nil
At the moment I have my routes setup as so:
resources :users do
resources :jobs
end
and the link I have is <%= link_to "Edit", edit_user_job_path(#user.id,#job) %>
What is interesting is that if I pass the object #jobs with an 's' on the end it will load the page correctly but when I click on the link will try and add all of that users job id's.
In my controller for edit I have:
def edit
#user = current_user
#job = #user.jobs.find(params[:id])
end
Any help really would be much appreciated :)
UPDATE
Okay I was defining the object on the wrong page of my controller (under edit instead of index). The issue I am now having is Couldn't find Job without an ID
I updated my controller index definition to:
def index
#user = current_user
#jobs = #user.jobs.all
#job = #user.jobs.find(params[:id])
end
And have in my view (jobs#index)
<% #jobs.each do |f| %>
...
<%= link_to "Edit", edit_user_job_path(#user.id,job) %>
...
<% end %>
Any advice would be much appreciated if you know where I am going wrong :)
That error means that #job is nil.
The link is to the edit path, and the controller code you've provided is from the edit action in the controller. It seems unlikely that the edit page links to itself.
Look at the code that's actually rendering that page (it will appear in your stack trace) and you'll find that #job is not set. I suspect that you are on the index page and have something like:
<% #jobs.each do |job| %>
...
<%= link_to "Edit", edit_user_job_path(#user.id,#job) %>
...
<% end %>
If that is the case, then the link should be to job, not #job, i.e.
<%= link_to "Edit", edit_user_job_path(#user.id,job) %>
(expanding on iHiD's comment with his own post)
Using the restful resources means that you are going with the rails defaults, which consequently means that the index page gives you a list of all Jobs, and by default no single special job. If you run rake routes from the command line, you get all the routes, with parameters that are set from the URI. It should give you something like this:
user_jobs GET /users/:user_id/jobs(.:format) jobs#index
As you can see, there is no :id (params[:id]) for the index action.