How do you build a helper method that looks like
-confirmation_for [#post, #comment] do |f|
= f.confirm "Post"
%p html here...
= f.edit "Edit"
and encapsulates two forms like
-form_for [#post, #commment] do |f|
= f.hidden_field :submission_state, :value => "confirmed"
= f.submit "Post"
%p html here...
-form_for [#post, #commment] do |f|
= f.hidden_field :submission_state, :value => "edit_requested"
= f.submit "Edit"
I don't think there's a way to get exactly what you're looking for without redefining form_for. Or writing your own version of it.
You can get close with FormBuilders or Partials or Helpers, but none of these concepts either alone will let you do what you're looking to do.
A FormBuilder will let you define methods to be invoked on the form, like confirm and edit, but they would be part of the same form. Unless you create two forms.
In the related helper file:
class ExampleFormBuilder < ActionView::Helpers::FormBuilder
def confirm
hidden_field(:submission_state, :value => "confirmed") + "\n" + submit "Post"
end
def edit
hidden_field(:submission_state, :value => "edit_requested") + "\n" + submit "Edit"
end
end
Sample usage:
- form_for [#posts,#comments], :builder => ExampleFormBuilder do |f|
= f.confirm
- form_for [#posts,#comments], :builder => ExampleFormBuilder do |f|
= f.edit
When used with a partial you could do something like this
partial:
- form_for [#posts,#comments], :builder => ExampleFormBuilder do |f|
= f.send(action)
view:
= render :partial => :confirmation_for, :locals => {:action => :confirm}
= render :partial => :confirmation_for, :locals => {:action => :edit}
You could then bundle both partial calls into another partial. But that's going a little too far.
Related
Edit:
It turns out, this was really an issue of syntax when using render for partials. It should be render partial:
I am following this blog post guide to implement the acts_as_commentable_with_threading gem.
http://twocentstudios.com/blog/2012/11/15/simple-ajax-comments-with-rails/
I have a character model with the following comment form partial in the show.html.haml view.
This line is giving me an undefined local variable or method for 'comment'
app/views/characters/show.html.haml
...
= render 'comments/form', :locals => { comment => #new_comment }
...
The hash inside the locals hash seemed off to me, so i changed the comment local variable to :comment. This worked fine, but I don't believe that is I am supposed to do.
When I do this, the form partial that is rendered also uses a comment local variable.
app/views/comments/_form.html.haml
.comment-form
= simple_form_for comment, :url => comment_path, :remote => true do |f|
= f.error_notification
= f.input :body, :input_html => { :rows => "3" }, :label => false
= f.input :commentable_type, :as => :hidden, :value => comment.commentable_type
= f.input :commentable_id, :as => :hidden, :value => comment.commentable_id
= f.error :base
= f.button :submit, :class => "btn btn-primary", :disable_with => "Submitting…"
Notice that the object passed into the simple_form_for method is also a local variable. This raised an error too, and i turned it into a symbol as well. Next, the comment.commentable_type raised an error, naturally, because comment was not defined. I cannot turn this into a hash because it is having a method call on it, right?
Whatever the answer is to the question is, it seems like I am going about this the wrong way. I shouldn't be turning things into symbols, when the problem is really that it isn't defined. Where should I define it? Where and how should I define it? I tried simply doing comment in the comments controller,
I am using rails 4.0.0, using simple form, and haml. Could it be a bad syntax for simple form?
EDIT: it was a second line rendering the comment partial that was raising the error. (everything being named comment was making it hard to tell where it was coming from.
= render 'comments/form', comment: #new_comment
= render 'comments/a_comment', collection: #comments, as: :comment
-# partial for a single comment.
%div.comment{ :id => "comment-#{comment.id}" }
%hr
= link_to "×", comment_path(comment), :method => :delete, :remote => true, :confirm => "Are you sure you want to remove this comment?", :disable_with => "×", :class => 'close'
%h4
= comment.user.username
%small= comment.updated_at
%p= comment.body
%h4
In your controller:
def show
#character = Character.find(params[:id])
#new_comment = #character.comments.build
end
Assuming there is a has_many relation between character and comment.
In your view:
render partial: 'comments/form', locals: { new_comment: #new_comment }
or
render 'comments/form', new_comment: #new_comment
In your partial:
= simple_form_for new_comment, remote: true do |f|
I would like to pass the form_for object to a partial:
<%= form_for #price do |f| %>
...
<%= render :partial => "price_page", :object => #price, :as => :f %>
...
<% end %>
When I call:
f.radio_button
Brings the error:
undefined method `radio_button' for #<Price:0x3cb1ed0>
How can I use f as I usually would in this partial?
Try passing form object as local
<%= render :partial => "price_page", :locals=>{:f=>f} %>
You can pass form builder object as a local variable like below,
<%= form_for #price do |f| %>
<%= render :partial => "price_page", :locals => { :f => f } %>
<% end %>
in your partial file you will be receiving form builder as a local variable "f", you can use like below,
<% f.radio_button, {} %>
I ran across this question trying to figure out how to get a form builder into a partial without an additional form tag. That's the primary use case I could think of for this question, so I'm adding this answer for future visitors.
To solve my problem, I have my form_for in my layout and I render my partial passing only the model. In my partial I use fields_for.
Looks (something) like this:
= form_for #price do |f|
...
= render partial: "price_page", object: #price, as: 'price %>
...
Then, my partial has this:
= fields_for price do |f|
...
Here is the relevant code from views/products/edit.html.erb:
<%= form_for(:product, :url => {:action => 'update', :id => #product.id}) do |f| %>
<%= render(:partial => "form", :locals => {:f => f}) %>
<%= submit_tag("Update Product") %>
<% end %>
from views/products/_form.html.erb:
<%= select_with_new_option(f, :shop, :name, :id) %>
and from helpers/products_helper.rb:
def select_options_with_create_new(objects, text_field, value_field, options={})
object = objects.to_s.singularize
all_objects = object.capitalize.constantize.all
all_objects = all_objects.sort_by(&options[:order_by]) if options.has_key?(:order_by)
select_options = all_objects.map{ |obj| [obj.send(text_field), obj.send(value_field)] }
select_options << [wrap_create_new_option("create new #{object}".titleize), "new_#{object}"]
options_for_select(select_options)
end
def wrap_create_new_option(str)
">> #{str} <<"
end
# By default, sorts by 'text_field'.
def select_with_new_option(f, object, text_field, value_field)
f.select(:"#{object}_id", select_options_with_create_new(object.pluralize, text_field, value_field, :order_by => text_field))
end
I expect the select box to be #product.shop_id by default, but this is not true (the first option is always the default value).
What am I missing ?
Alright, I got it. Just remove options_for_select(select_options) in the select_options_with_create_new method.
The options_for_select assembles the 2-d array select_options into a string of html options, which is accepted by the select form helper, however, without assigning the selected attribute. Just pass the 2-d array to the select method as the second argument, it would assign the selected automatically.
I think that, in this case, all you need to do is make sure you set #product in your edit action:
def edit
#product = Product.find(params[:id])
end
Then change your first line:
<%= form_for(:product, :url => {:action => 'update', :id => #product.id}) do |f| %>
to
<%= form_for(#product, :url => {:action => 'update', :id => #product.id}) do |f| %>
The first code block will certainly update the proper product, but you'll only see the current values reflected if you generate a form_for #product, with that #product variable set.
Hey, I have a bunch of forms for different controllers all on the same page. This is because theres a bunch of models which stack on each other and this is the best way to edit them.
I'm using partials to include each form into the view, passing locals for the object and url params to be used by form_for
The first (top level) form partial:
- form_for :person, local_assigns[:obj], :url => local_assigns[:url] do |person_form|
= person_form.error_messages
%p
= person_form.label :first_name
= person_form.text_field :first_name
%p
= person_form.label :last_name
= person_form.text_field :last_name
%p
= person_form.submit 'Save'
- if local_assigns[:obj]
= link_to 'Destroy', local_assigns[:obj], :confirm => 'Are you sure?', :method => :delete
which is included into the view via
= render :partial => 'person_form', :locals => { :url => { :controller => 'people', :action => 'create' }, :obj => #person }
to create a new Person, and
= render :partial => 'person_form', :locals => { :url => "people/update/#{person.id}", :obj => person }
within this block:
- Person.all.each do |person|
to edit all existing people.
My problem is that every form, excluding the first (top level) person create and edit forms, the action gets set to the current controller.
The form_for partials for all other forms are very similar:
- form_for :day, local_assigns[:obj], local_assigns[:url] do |day_form|
= day_form.error_messages
%p
= day_form.label :effective_date, 'Date'
= day_form.datetime_select :effective_date
= day_form.label :person_id
= day_form.collection_select(:person_id, Person.all, :id, :full_name, { :selected => local_assigns[:ref] ? local_assigns[:ref].id : 1 } )
%p
= day_form.submit 'Save'
- if local_assigns[:obj]
= link_to 'Destroy', local_assigns[:obj], :confirm => 'Are you sure?', :method => :delete
included via
= render :partial => 'day_form', :locals => { :url => { :controller => 'days', :action => 'create' }, :obj => #day, :ref => person }
for the create form, and
= render :partial => 'day_form', :locals => { :url => "days/update/#{day.id}", :obj => day, :ref => person }
for the edit forms.
The two models, Person and Day, are linked together by Person having many :days and Day belonging to :person. The associations are all setup correctly and working, the problem is with rendering all the forms with different controllers to generate the correct action url.
Any suggestions?
(I'm using haml btw)
Your syntax seems a bit weird to me. If pass an actual object to form_for, it automatically figures out where to submit the form to depending on whether it is a new or existing record.
Something like this should do the trick for you:
# In your controller
# Create a new record
#new_person = Person.new
# And pull out all existing records for editing
#all_people = Person.all
# In whatever view is wrapping your form partials
%h1 Create a new person
= render :partial => 'person_form', :locals => { :person => #new_person }
%h1 Or edit any of the existing people
= render :partial => 'person_form', :collection => #all_people, :as => :person
# In your `person_form` partial
form_for person do |person_form|
... # your usual code here
In the last line of code, where you call form_for, you pass in an actual object - and the URL to submit the form to is inferred based on whether the record is new (as it is for #new_person) or already exists in the database (as it does for each person in #all_people).
Does that make sense? Have I understood the problem correctly?
If Person and Day are specified as resources in your routes, form_for can infer the correct controller and action from the object. For example...
In your person partial:
- form_for #person do |f|
= f.error_messages
= # ...etc...
In your day partial (where Person has_many Days):
- form_for [#person, #day] do |f|
= f.error_messages
= # ...etc...
How can I set the class and id attribute for the form element through semantic_form_for?
The following code:
<% semantic_form_for (#meetingsearch), :class => "new_meeting_search", :id => "meeting_search" do |f| %>
gives me:
<form action="/meetingsearches" class="formtastic meetingsearch" id="new_meetingsearch" method="post">
This should do what you need (untested):
<% semantic_form_for #meetingsearch, :html => { :class => "new_meeting_search", :id => "meeting_search" } do |f| %>
For clarification, semantic_form_for wraps around Rails' built in form_for, so this is exactly how you do it in regular Rails forms also:
<% form_for #meetingsearch, :html => { class => "new_meeting_search", :id => "meeting_search" } do |f| %>