I've been trying to create an order confirmation page for my rails app, and am not quite sure how to go about it in a restful way.
There were a few answers on this question that got me halfway there, but the problem was that I wasn't quite sure how to set up the form in the rails view so that it would take the user to a confirmation page with all their details instead of a create action.
Right now my view is simple:
<% form_for :order do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :first_name %><br />
<%= f.text_field :first_name, :size => 15 %>
</p>
<p>
<%= f.label :last_name %><br />
<%= f.text_field :last_name, :size => 15 %>
</p>
(Be sure to enter your name as it appears on your card)
<p>
<%= f.label :card_type %><br />
<%= f.select :card_type, [["Visa", "visa"], ["MasterCard", "master"], ["Discover", "discover"], ["American Express", "american_express"]] %>
</p>
<p>
<%= f.label :card_number %><br />
<%= f.text_field :card_number %>
</p>
<p>
<%= f.label :card_verification, "Card Verification Value (CVV)" %><br />
<%= f.text_field :card_verification, :size => 3 %>
</p>
<p>
<%= f.label :card_expires_on %><br />
<%= f.date_select :card_expires_on, :discard_day => true, :start_year => Date.today.year, :end_year => (Date.today.year+10), :add_month_numbers => true %>
</p>
<p><%= f.submit "Submit" %></p>
What things should I be doing to direct the user to a confirmation page that shows all the order details?
Thanks!
Kenji
There were a few answers on this
question that got me halfway there,
but the problem was that I wasn't
quite sure how to set up the form in
the rails view so that it would take
the user to a confirmation page with
all their details instead of a create
action.
Directing a form to a non standard page is pretty simple.
Add a url option form_for.
Such that
<% form_for :order do |f| %>
becomes
<% form_for :order :url => {:action => "confirm"} do |f| %>
You'll need to crate the confirm action in your routes, but that only involves this:
map.resources :orders, :collection => {:confirm => :get}
All you need now is a basic controller action and a view:
def confirm
#order = Order.new(params[:order])
unless #order.valid?
render :action => :new
else
end
end
Your view should look almost identical to the show view, with the addition of a form submitting #order to the create action.
Why don't you pull the confirmation via ajax for example, pull the result and put it as an overlay div, upon confirmation submit the original values in the form.
If you still need to do it your way then check wizardly, it's exactly designed for such uses.
I would like to update the answer for more elegant Rails 4 or up.
I hope it will help newbies like me. Ruby is awesome! :)
routes.rb
resources :orders do
collection do
post 'confirm'
end
end
orders_controller.rb
def confirm
#order = Order.new(order_params) # GET THE POST parameters
render :new if #order.invalid? # Return if false
end
form.html.erb
<%= form_for #order, url: {action: 'confirm'} do |f| %>
Related
I would like to put a form on one of my pages, but don't want to use form_for to update a model. I am basically using this like a filtering/searching system, where the user inputs something, and the page changes based on what the user input. I know this is a pretty simple problem, but I'm also a little new to Rails.
Note: I have the ability to filter the results if I can just get the input value. I just need access to the input value in my Controller.
Just use form_for with a symbol as argument rather than an instance variable.
You can access the form data in your controller by referencing your params, just like you normally would. Let's say you have a form kinda like this:
<%= form_for :search do |f| %>
<%= f.text_field :query %>
<%= f.submit %>
<% end %>
You'll then get the contents of the search form by calling params[:search][:query] in your controller.
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for
you need meta_search
<%= form_for #search, :url => articles_path, :html => {:method => :get} do |f| %>
<%= f.label :title_contains %>
<%= f.text_field :title_contains %><br />
<%= f.label :comments_created_at_greater_than, 'With comments after' %>
<%= f.datetime_select :comments_created_at_greater_than, :include_blank => true %><br />
<!-- etc... -->
<%= f.submit %>
<% end %>
I've got a form for an order and with this, the user have to choose a category then associated product. As I want it to be dynamic, I wrote this :
_form.html.erb
<%= form_for(#order) do |f| %>
<%= f.collection_select :category_id, #categories_list, :id, :name, :prompt => "Selectionner" %>
<%= render :partial => 'products' %>
<%= f.submit 'Enregistrer', :class=>'button add'%>
<% end %>
_products.html.erb :
<%= form_for(Order.new, :remote => true) do |f| %>
<% if !#products.blank? %>
<%= f.label 'Produit :' %><br />
<%= f.select :product_id, #products.collect{ |s| [s.name,s.id]}, :prompt => "Selectionner" %>
<% else %>
<%= f.label 'Produit :' %><br />
<%= f.select :product_id, '' %>
<% end %>
<% end %>
in the controller :
def update_product_select
#products = Product.where(:category_id=>params[:id]).order(:name) unless params[:id].blank?
render :partial => "products", :layout => false, :locals => { :products => #products }
end
For the dynamic part, it works.
But when I click on the submit button, the product ID is not sent !
Can you tell me how is it possible for me to combine dynamic menu and form submitting please ?
Thanks.
I don't understand why you need two forms. Something is wrong here.
Explain a bit more on the workflow if possible.
I see you should either make use of fields_for if you are trying to edit other associated instances of #order.
For a dynamic menu, have a javascript event binding on the category that updates the products list based on the category. Let me know if you need an example.
I think you are looking for this: http://railscasts.com/episodes/88-dynamic-select-menus-revised
It needs a subscription, but you can also see the previous episode to get an idea if you dont want to subscribe.
I have a form in rails 3.2 for creating users in a company. Each company has sites. A site is assigned to a user via a collection_select, and then each site has a number of departments which are selected using check boxes. A user belongs to a site and can belong to many departments from that site.
I need the department check box options to change dependant on the selected value in the sites collection_select.
I have been trying to do this by using :onchange of the collection_select to call a partial containing the departments. I've managed to get this to work but only displaying all departments for the company. Not departments for each individual site. I don't understand how to pass the selected site to the partial so it can return the correct departments (if this is even possible).
My code so far is:
Form:
<%= form_for #user, :url => front_admin_users_path do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<%= f.collection_select :role, User::ROLES, :to_s, :humanize %>
<%= f.collection_select :site_id, current_user.company.sites.all, :id, :name, {}, :onchange => "jQuery.get('/load_department')", :remote => true %>
<div id="department_frame"></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
users_controller.rb:
def load_department
respond_to do | format |
format.js {render :layout => false}
end
end
load_department.js.erb:
$("#department_frame").html( "<%=j render(:partial =>"department") %>" );
_department.html.erb:
<% current_user.company.sites.each do |site| %>
<% for department in site.departments %>
<%= check_box_tag "user[department_ids][]", department.id, current_user.department_ids.include?(department.id) %>
<%= department.name %>
<% end %>
I have googled for days trying to find the solution but can't find exactly what I need. The nearest thing I've found is Ryan Bate's dynamic collection selects revised using the grouped_collection_select method (which I've used in another part of the project), but this doesn't seem to deal with select boxes. Any help would be appreciated. Thanks.
You could try passing the site_id as a parameter in the URL to the AJAX call and then read that value off in the controller. In the controller method, you can also then get the list of departments for that particular site and then pass that off to the view that is being rendered.
I have edited the relevant parts of the code to explain what I mean better. Hopefully this makes sense :)
Form
<%= f.collection_select :site_id, current_user.company.sites.all, :id, :name, {}, :onchange => "jQuery.get('/load_department?site=' + $('#user_site_id').val())", :remote => true %>
users_controller.rb:
def load_department
site = params[:site]
#departments = Site.find(site).departments
respond_to do | format |
format.js {render :layout => false}
end
end
_department.html.erb:
<% for department in #departments %>
<%= check_box_tag "user[department_ids][]", department.id, current_user.department_ids.include?(department.id) %>
<%= department.name %>
<% end %>
I asked a question similar to this one about a week ago, but this is a slightly different perspective on it. The nature of the question involves being redirected to the correct controller.
I have a single resource, posts, and I have 4 different categories these posts can be under. I want each of these categories to be particular to a single controllers, and so I have the following in my routes.rb:
resources "code", :controller => :code_posts, :as => :code
resources "sports", :controller => :sports_posts, :as => :sports
resources "gaming", :controller => :game_posts, :as => :gaming
resources "the-nation", :controller => :personal_posts, :as => :the_nation
So now I can access posts through URLs like, for example, /code/1, /sports/34 to access the same post resource, but with each controller focusing on a single scope, namely a particular category.
This is all well and good, but my issue comes up when I try to edit or save particular posts. I have the following partial _form.html.erb (rendered in the new and edit views) in all the view folders for their particular controller:
<%= form_for #post do |f| %>
<div class="field">
<%= f.label :author %><br/>
<%= f.text_field :author %>
</div>
<div class="field">
<%= f.label :title %><br/>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :category %>
<%= f.select :category_id, Category.all.collect {|c| [c.name, c.id] }, {:include_blank => true} %>
</div>
<div class="field">
<%= f.label :summary %><br/>
<%= f.text_area :summary, :rows => 5 %>
</div>
<div class="field">
<%= f.label :body %><br/>
<%= f.text_area :body %>
</div>
<div class="field">
<%= f.label :tag_tokens %><br/>
<%= f.text_field :tag_tokens, "data-pre" => #post.tags.map(&:attributes).to_json %>
</div>
<div class="field">
<%= f.submit "Submit" %>
</div>
<% end %>
So whenever I create or update a post, through whichever controllers, I always get redirected back to /posts/4, /posts/123, /posts/:id, whatever. I want to get redirected to the particular controller the post being edited or created lives under. So if I go to /code/new, and submit the new post, I want to be redirected to /code/1234, and not /posts/1234. How can I do this? For some reason I'm just having major mental mind blocks this morning. Thanks.
EDIT Updated <%= form_for #post do |f| %> to <%= form_for #post, :url => code_url(#post) do |f| %> and it works for /code/1/edit but not /code/new. When trying to access a new post form, I get the following error:
No route matches {:action=>"show", :controller=>"code_posts", :id=>#<Post id: nil, author: "Les Peabody", summary: nil, body: nil, created_at: nil, updated_at: nil, title: nil, category_id: 1, slug: nil>}
This is my CodePostsController#new method
def new
#post = Post.new(:category => Category.find_by_name("Programming"), :author => current_user.full_name)
end
You may specify the url in the form
<%= form_for #post, :url => gaming_path do |f| %>
You may use inheritance on the model.
The path in you form is determined by the class name, and in this case it is post.
If they mach with resources naming it should generate proper paths as well.
The dirty hack may be keeping objects path in it, I saw someone do that, but I do not recommend it too much.
I think the reason is the form_for method which takes for the update action as default the name of the parameter (here post) it gets.
So to change that, you have to add at the beginning (for the example resource code) the following:
<%= form_for #post, :url => code_path(#post) do |f| %>
This is of course only the URL for an existing object, the URL for a new object should be different. It should be there new_code_path (and no argument). So your partial should only contain the fields and labels, not the form_for call, because the URL should be different then.
You should look at the output of the call in the shell: bundle exec rake routes and search for the correct paths in the output.
So, ultimately what is important is how the form gets turned into HTML. If you look at the differences between a form that is meant for editing, and a form that is meant for a new object, there is only one thing that is ever really different that matters - the action URL.
In the case of a new form, the form tag should look something like:
<form accept-charset="UTF-8" action="/code" class="new_post" id="new_post" method="post">
and in the case of an edit form:
<form accept-charset="UTF-8" action="/code/1" class="edit_post" id="edit_post_1" method="post">
The only thing that matters to rails however is the name of the input elements (which are constant in both forms) and the action attribute in the form tag. That tells Rails whether or not it's rendering the edit or create action.
Since we're splitting up control of a single resource through multiple controllers, the standard form_for #post will not suffice since Rails can no longer automate the rendering process through convention (as we're doing a very unconventional thing). It is necessary to do some manual labor. The following will do the trick.
Convert the partial to the following:
<%= form_for #post, :url => path do |f| %>
<div class="field">
<%= f.label :author %><br/>
<%= f.text_field :author %>
</div>
<div class="field">
<%= f.label :title %><br/>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :category %>
<%= f.select :category_id, Category.all.collect {|c| [c.name, c.id] }, {:include_blank => true} %>
</div>
<div class="field">
<%= f.label :summary %><br/>
<%= f.text_area :summary, :rows => 5 %>
</div>
<div class="field">
<%= f.label :body %><br/>
<%= f.text_area :body %>
</div>
<div class="field">
<%= f.label :tag_tokens %><br/>
<%= f.text_field :tag_tokens, "data-pre" => #post.tags.map(&:attributes).to_json %>
</div>
<div class="field">
<%= f.submit "Submit" %>
</div>
<% end %>
The path variable in there is a variable passed in through the :locals mechanism in the partial render, like so:
new.html.erb
<%= render :partial => "form", :locals => {:path => code_index_path} %>
and edit.html.erb
<%= render :partial => "form", :locals => {:path => code_path(#post)} %>
The nice thing with this solution is you can DRY up the code too by placing _form.html.erb in app/views/layouts or app/views/posts and reuse it in all of the new and edit views for all controllers that manipulate the Post resource in a consistent fashion. So rather than having:
<%= render :partial => "form", :locals => {:path => code_path(#post)} %>
we have:
<%= render :partial => "layouts/form", :locals => {:path => code_path(#post)} %>
I am trying to edit my events model. Here is what I have for the edit:
Edit Event
<%= form_for(#event) do |f| %>
<%= render 'fields', :f => f %>
<div class="actions">
<%= f.label :name %><br />
<%= f.text_field :name %>
<%= f.label :description %><br />
<%= f.text_field :description %>
<%= f.submit "Update" %>
</div>
<% end %>
I made the route as so:
resources :events do
member do
post :attend
post "/remove_attendee/:user_id" => "events#remove_attendee", :as=>:remove_attendee
post "/edit" => "events#edit"
end
But I am getting a no method error. NoMethodError in Events#edit and it is on line 3 which is the form_for place. What am I doing wrong?
The error is not in your view, the form_for tag calls some methods on the resource passed in, in this case #event. Make sure that #event is not nil and that the lookup to pull up the resource is working as intended. Again, seeing you controller/model/etc. code along with the full error would help.
Check that you are setting #event in your controller.