I am relatively new to Ruby on Rails, and am trying to set up a form with a new action on an existing controller.
My existing model and controller is called 'projects', and I created a new action in the controller called 'queue'. The goal is that a user can filter projects by different users using '/queue/username'.
routes.rb
match 'queue/:username' => 'projects#queue'
projects_controller.rb
def queue
if (params[:user_id].blank? && params[:user_id].nil?)
#projects = Project.find_all_by_user_id(User.where(:username => params[:username]).pluck(:id))
else
#projects = Project.find_all_by_user_id(params[:user_id])
end
end
queue.html.erb
<%= form_tag("queue", :id => "select_user", :method => "post") do %>
<%= hidden_field_tag('user_id', '') %>
<%= text_field_tag('user', nil, :placeholder => 'Enter a user...', class: "users",
data: {autocomplete_source: User.order(:lastname, :firstname).map { |u| {:label => u.firstname + " " + u.lastname, :id => u.id} }}) %>
<% end %>
When I submit this form it submits as 'queue/queue', and in order to have a direct link to this action I need to do:
<%= link_to "Queue", queue_path + "/" + current_user.username.to_s %>
Which I know is not correct.
My question is how do I get the form to submit as 'queue/username'? Should this be in a new 'queue' controller to handle routing separately? Any help is appreciated.
Rails version 3.2.13
Ruby version 1.9.3
Two places to revise:
Route. Better to use static route for POST without parameter, and specify POST
match 'projects/queue' => 'projects#queue', via: :post
Form tag. You need to specify the path
<%= form_tag "projects/queue", method: :post do %>
Better not to use div id, if you have to, use it like this
<%= form_tag "projects/queue", method: :post, html: {id: "select_user"} do %>
The answers from Josh and Billy can accomplish this well, to throw another hat into the mix I would suggest making this a route based off of the projects. Assuming your routes are restful and contains projects as resources projects:
resources :projects do
post "queue", :on => :collection
end
What this does is add the route projects/queue to your routes since it is based off of the collection instead of a member. See: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions for more info.
It is worth noting why this happens. It is because the the route you are originally posting to is only queue when what you want is queue/:id since the id is specified via a url parameter you should use a get request. But since it looks like you are posting the id via a post request this is a little confusing. Choosing just to use a post request you do not need the url parameter like Billy suggested and I also agree since this will allow you to keep it a but more succinct.
Hope this helps!
Edit For username usage
Changing your form to:
<%= form_tag("queue", :id => "select_user", :method => "post") do %>
<%= hidden_field_tag(:username) %>
<%= text_field_tag('user', nil, :placeholder => 'Enter a user...', class: "users",
data: {autocomplete_source: User.order(:lastname, :firstname).map { |u| {:label => u.firstname + " " + u.lastname, :username => u.username} }}) %>
<% end %>
Assuming the JS is working as it should, you just need to populate the hidden field with the :username value instead of :id. Then on the controller side of things:
def queue
#projects = Project.find_all_by_username(params[:username])
end
Hope this makes sense!
You can change your route to something like
match 'queue/:username' => 'projects#queue', :as => 'projects_queue'
And then have the destination be
projects_queue(current_user)
Related
I've completed Hartl's Rails Tutorial and am now working on a project that uses the simple_calendar gem.
I want to add a dropdown to allow users to quickly navigate to a specific month and year without having to click the "next" link multiple times.
So far I have managed to create the dropdown form but I can't figure out how to set up the controller to get the url with the form parameters on submit.
This is in the partial I created that renders at the top of the monthview calendar:
<%= form_with do |form| %>
<%= select_month( Time.now, {:field_name => 'month', :id => 'month'} ) %>
<%= select_year( Date.today, {:start_year => Time.now.year,:end_year => Time.now.year+3} ,{:field_name => 'year', :id => 'year'} ) %>
<%= submit_tag("Go", :id=>"button", :class=>"date-button", :name=>"submit") %>
<% end %>
Can anyone explain how I can write the controller code to pass those parameters so that the user goes to a link like https://mycalendar.com/calendar?year=2021&month=june
Thank you for your help.
You can read the parameters directly
<%= form_with do |form| %>
<%= select_month( params[:month].present? ? Date.strptime(params[:month], '%B') : Time.now, {:field_name => 'month', :id => 'month'} ) %>
<%= select_year( params[:year].present? ? Date.strptime(params[:year], '%y') : Date.today, {:start_year => Time.now.year,:end_year => Time.now.year+3} ,{:field_name => 'year', :id => 'year'} ) %>
<%= submit_tag("Go", :id=>"button", :class=>"date-button", :name=>"submit") %>
<% end %>
But you have to add a checking for invalid date/parameters, so better do the parsing in the controller side
You can grab parameters sent to a controller via params[:parameter]. This works for both GET and POST requests. In a GET request these parameters are passed through the url, ie. /calendar?year=2021&month=june, whereas in a POST request they are passed in a formBody object.
One of the best things to do in Rails is to look at your console when a request is made. It will tell you the route, controller, action, and parameters in the log.
For a GET request you might use the method below to get the parameters.
# controller
def calendar
year = params[:year]
month = params[:month]
end
Generally a form would be sent via a PUT (create) or PATCH (update) request (known together as POST). In that case you might have a model object that the form fields would be stored in, and you would grab the parameters through a sanitization procedure. This sanitization is very important to ensure malicious code isn't passed through to your database.
def create
#event = Event.new(event_params)
...
end
private
def event_params
params.require(:event).permit(:month, :year, :title)
end
Your form_with might need a url: '/calendar' and even a method: :get to turn it into a GET request and send it to the correct route.
For RESTful purpose, I made my routing just like this:
routes.rb
match 'shops/:sort/:genre/:area', :to => 'shops#index'
But what if genre was empty?
Isn't it going to redirect to example.com/shops/newest_first//california?
How can I solve this kind of routing and parameters problem?
Can anyone show me good example?
view
<%= form_tag shops_path, :method => :get do %>
<%= select_tag :sort, options_from_collection_for_select(Sort.all, 'id', 'name', params[:sort]), :prompt => "Newest first" %>
<%= select_tag :genre, options_from_collection_for_select(Genre.all, 'id', 'name', params[:genre]), :prompt => "all" %>
<%= select_tag :area, options_from_collection_for_select(Area.all, 'id', 'name', params[:area]), :prompt => "all" %>
<% end %>
Another View
While restful routing is the most intuitive and conventional, it does not always fit our needs.
In your case I'd suggest using query parameters instead of a strict restful route.
example.com/shops will receive 3 query parameters: sort, genre and area, so a URL may look like example.com/shops?area=california&sort=newest_first
The in the index action of you controller you can query for the existence of these parameters in the following manner:
def index
if !params[:sort].blank?
#shops = #shops.sort(params[:sort])
end
if !params[:area].blank?
#shops = #shops.where(:area => params[:area])
end
.
.
.
end
This way you are well protected against missing parameters in your controller, but still you are able to supply whatever data the user requests.
On a side note: be sure to check that the params hash you're using contains only values you are willing to accept.
Hi
I have asked a question similar to this before but never got it resolved. So I am trying again.
This seems like it should be so simple. I am not using Rails 3 yet BTW.
All I want to do is have a drop down menu and when a person chooses that location and presses "go" they go to that page.
<% form_tag installation_path([:id]), :url => { :action => "show" }, :method => :get do %>
<%= select_tag :id, options_from_collection_for_select(Installation.find(:all), :id, :name) %>
<%= submit_tag 'Go' %>
<% end %>
This becomes the issue: http://localhost:3000/installations/id?id=1&commit=Create. It can't find the :id. I just don't know how to route this correctly. It seems like this shouldn't be that difficult.
Any help would be great. Thanks.
I think there might be a problem with your form_tag. It seems you're defining the path twice.
Both
installation_path([:id])
and
:url => { :action => "show" }
are used to generate the path but I don't think you should be using both. Just go with
installation_path([:id])
or
:url => { :controller => "installations", :action => "show", :id => id }
You need to create and use a new "show" route that is not based on the installation id (and doesn't collide with Rails resource routes), and continue to send the installation id into the controller's show action as part of the params object.
In routes.rb,
get 'show_installation', to: 'installations#show'
In your view,
<% form_tag show_installation_path, :method => :get %>
...
I'm trying to put together a form_tag that edits several Shift objects. I have the form built properly, and it's passing on the correct parameters. I have verified that the parameters work with updating the objects correctly in the console. However, when I click the submit button, I get the error:
ActiveRecord::RecordNotFound in ShiftsController#update_individual
Couldn't find Shift without an ID
My route for the controller it is calling looks like this looks like this:
map.resources :shifts, :collection => { :update_individual => :put }
The method in ShiftsController is this:
def update_individual
Shift.update(params[:shifts].keys, params[:shifts].values)
flash[:notice] = "Schedule saved"
end
The relevant form parts are these:
<% form_tag( update_individual_shifts_path ) do %>
... (fields for...)
<%= submit_tag "Save" %>
<% end %>
Why is this not working? If I browse to the url: "http://localhost:3000/shifts/update_individual/5" (or any number that corresponds to an existing shift), I get the proper error about having no parameters set, but when I pass parameters without an ID of some sort, it errors out.
How do I make it stop looking for an ID at the end of the URL?
I think that you need to tell the form tag helper you want to use PUT instead of POST
<% form_tag( update_individual_shifts_path, :method => :put) do %>
... fields ....
<%= submit_tag "Save" %>
<% end %>
Amazingly, it turns out that I was able to fix this by a combination of renaming the method and passing a dummy variable. Changes were to the lines:
form.html.erb:
<% form_tag( poop_individual_shifts_path ) do %>
routes.rb:
map.poop_individual_shifts "poop_shifts", :controller => 'shifts', :action => "poop_individual", :method => "put", :id => 4
map.resources :shifts
There I pass it an ID of 4 every time, it doesn't matter, it's not actually doing anything with the shift object it goes and grabs, it's just ... I don't know, a hack, I guess.
shifts_controller.rb:
def poop_individual
I have the following form_for declaration in a Rails site I am building:
form_for(form_question, :url => { :controller => "form_questions", :action => "edit", :id => form_question.id }) do |f|
but the site renders;
<form action="/form_questions/1/edit">
why is it putting the '/1/' before the "edit" in the action url?
Simple Answer. RESTful routes.
Basically you have defined resources :form_questions in config/routes.rb and that is transforming, automagically, your URL to make it RESTful.
I would recommend using the RESTful helpers provided to you, like:
<% form_for(#form_question) do %>
<%= f.text_field :question %>
...
<% end %>
Which will generate a URL to either create or update depending on if the #form_question response to new_record? is true or false respectively. It'll also do other things, like give the form tag a different id attribute based also off what new_record? returns.