Binding multiple objects to a Rails form - ruby-on-rails

I'm following this tutorial - http://tutorials.jumpstartlab.com/projects/blogger.html#i2:-adding-comments. It is building a sample blog app and so far has an Article model and is adding a Comment model. An Article has_many Comments.
They want to be able to create the comment using a form on the articles page:
<h3>Post a Comment</h3>
<%= form_for [ #article, #comment ] do |f| %>
<p>
<%= f.label :author_name %><br/>
<%= f.text_field :author_name %>
</p>
<p>
<%= f.label :body %><br/>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
I don't understand how this form works though. It binds two objects to the form, and it says that it'll submit to article_comments_path, which will POST to comments#create.
How does this work? How does it know what to do when it has two objects? Does the order of the objects in the array matter? Can you have more than two objects?

you are probably going to use nested resources:
resources :articles do
resources :comments
end
[ #article, #comment ] is an array which will build a nested route. Like:
/articles/1/comments
article_comments_path maps to the index action (GET) and to create action(POST)
dorake routes to see what i'm talking about:
article_comments_path GET /articles/:article_id/comments(.:format) comments#index
POST /articles/:article_id/comments(.:format) comments#create
So essentially, you use [ #article, #comment ] when you have nested resources to create routes helpers. the order of the objects matter as it is article_comments_path and NOT comment_articles_path.
whether you can bind more than 2 objects to the form, it depends, I think, on how deeply nested are your resources
UPDATE:
you are using a form. and forms are for either to create or update objects depending on whether the object exists or not. The submit button will trigger that. This article sums it pretty good
"When the user clicks submit on a form, Rails invokes either the create (for a new record) or update (for an existing record) action in the associated controller. Forms are, by default, submitted with an HTTP POST command, and all the information that was entered in the form is provided in a hash called params, which is available to the controller."

Related

How to create a new category within the post form?

I need to create a new blog category in the blog post form only when the new blog post does not match the listed categories. I am creating two form_for methods in rails but it shows errors.
How to implement this on ruby on rails.
I am expecting the output as, if we are creating a blog post, suppose our post matching category is not listed in the select. we need need to create a new category at the same time.
You're probably looking for accepts_nested_attributes_for. If you add accepts_nested_attributes_for :category in your Post model, you'll be able to use fields_for in your post form. You didn't paste any code so this may not work without some adjustments for your specific Rails app, but you'd generally end up with a form like this:
<%= form_for #post do |post_form| %>
Title: <%= post_form.text_field :title %>
(...other post fields here...)
<%= fields_for :category, #post.category do |category_fields| %>
Category Title: <%= category_fields.text_field :title %>
<% end %>
<%= post_form.submit %>
<% end %>
Depending on your use case you may want to set #post.category to Category.new in your controller, and you'll want to whitelist the nested parameters in your controller if you're using strong params. See Nested Forms on Rails Guides for an example of this. You'll also want handle selecting an existing category separately from this.

Rails - Multiple forms, different Models (Objects), one submit button

I have a view with 3 forms, Schedules, Workouts and Exercises, all behaving like an edit form, each. And one submit(save) button in the all the view.
When I click on the save button. Every data changed on those forms should be updated after click.
What is the best solution for this ? Javascript updating each data separated ? How to do that ? Is there a more Rails way to do this easily ?
My difficulty is how to integrated all those models in one view, while all this is happening in the show(view) from the Student model.
If you're implementing something like a profile / edit page (where you can save all the records at once), the two ways I would look at would either be to save the forms via Ajax, or use a single submit method to handle them
Ajax
The ajax method would be the most conventional:
Every form you submit will go to the form's own update method in the backend
Each form could be handled by a single button, but it's best to split them up
#app/controllers/profile_controller.rb
def edit
#schedules = Schedule.all #-> not sure how many records you're using
#workouts = Workout.all
#exercises = Exercise.all
end
#app/views/profile/edit.html.erb
<%= form_for #schedule do |f| %>
<%= f.text_field :test %>
<% end %>
# -> other forms
<%= button_to "Save", "#", id: "save" %>
#app/assets/javascripts/application.js
$("#save").on("click", function() {
$("form").submit(); // we'll have to define the form to submit
});
Single
If you submit all the forms as one, you'll have to encase them all in a single form, as sending different errors. This could be achieved by using _, and handled in the backend by looping through the different params, saving each one individually.
I'd do this:
#app/controllers/application_controller.rb
def submit
types = %w(schedules exercises workouts)
for type in types do
type.constantize.update_attributes()
end
end
This allows you to create a form with the different data types submitted in the same action:
#app/views/profile/edit.html.erb
<%= form_tag profile_submit_path do %>
<%= fields_for #schedules do |f| %>
<%= f.text_field :title %>
<% end %>
# -> fields_for for the other objects
<% end %>
This will allow you to send the updated objects to your controller, allowing them to submit
If all of your models (Schedules, Workouts and Exercises) are associated, using fields_for should be a good option.
From the above link:
<%= form_for #person do |person_form| %>
First name: <%= person_form.text_field :first_name %>
Last name : <%= person_form.text_field :last_name %>
<%= fields_for :permission, #person.permission do |permission_fields| %>
Admin? : <%= permission_fields.check_box :admin %>
<% end %>
<%= f.submit %>
<% end %>
Read the guides.
You could have some simple javascript that iterates over all form tags and submits each of them.
Alternatively, if you are going to use javascript anyways, you could follow an AJAXish auto-save approach upon changing any field.
But I think it might be cleaner if you just had one form for multiple models, using fields_for.

Form has_many in text_area

I have the following situation:
A Order has many Pages. I want to let the User to paste a bunch (20+) URLs (it's a Page attribute) that they might have in a doc file into a text area.
Right now I am not using a Form associated with an Order object, because I fail to see how I can do a nested form of the URLs if those are inside a text area.
I have seen a similar question has been asked before here: Rails: Using a Textarea for :has_many relationship , but I fail to see how would I code the view and model in order to do so.
So, if I have this:
Order has_many Pages
And a form like this:
<%= form_for #order do |f| %>
<%= f.text_area :page_urls?? %> # This would let the user paste X URLs, which would be
# used to create X Pages associated with the Order.
<% end %>
You could retain the view code that you have:
<%= form_for #order do |f| %>
<%= f.text_area :page_urls %>
#other field and submit button
<% end %>
In your model, you'll need to do the following:
attr_accessor :page_urls
after_validation do
if page_urls
parse_page_urls.each do |url|
pages.create(url: url)
end
end
end
def parse_page_urls
#use regexp to extract urls from page_urls string and return an array of url strings
end
The accessor is defined so that you can use :page_urls in your form_builder. You could set easily validations in your model for :page_urls that way too.
Once order has been validated, it will create page objects according to the number of urls extracted from the page_urls attribute.
You could refer to this for some help with using regexp to extract the urls from the string.
Hope that helps!
This is a job best handled with nested form. It will let you submit attributes of a has_many relationship model from the parent model, like you wish to do. For example, from its docs:
Imagine you have a Project model that has_many :tasks. To be able to use this gem, you'll need to add accepts_nested_attributes_for :tasks to your Project model. If you wish to allow the nested objects to be destroyed, then add the :allow_destroy => true option to that declaration. See the accepts_nested_attributes_for documentation for details on all available options.
This will create a tasks_attributes= method, so you may need to add it to the attr_accessible array (attr_accessible :tasks_attributes).
Then use the nested_form_for helper method to enable the nesting.
<%= nested_form_for #project do |f| %>
You will then be able to use link_to_add and link_to_remove helper methods on the form builder in combination with fields_for to dynamically add/remove nested records.
<%= f.fields_for :tasks do |task_form| %>
<%= task_form.text_field :name %>
<%= task_form.link_to_remove "Remove this task" %>
<% end %>
<%= f.link_to_add "Add a task", :tasks %>
In response to your comment:
In order to do something like that, you would need to do processing in the controller to separate the URL's, then make a new Page object associated with #order object. Unfortunately, there isn't a way to do this without post-processing, unless you do it with JS on the client side with hidden inputs.

Anyone know how to save many objects in one form?

I am trying to save many new objects in a form to one pre-existing parent object.
- form_for :parent_object do |f|
This is the beginning of my form. And then within it, I would do:
- 2.times do
- fields_for :child_object do |f|
Now if I were to save this, it would render as an ParentObject_Controller Update action which would fail because Update doesn't identify new objects.
So if I wanted to render the appropriate Save action, I would have to set up like this :
- form_for [#parent_object, #child_object] do |f|
- 2.times do
- fields_for :child_object do |f|
This form then renders the Save action, but only saves the last child_object.
I would show you my controller, but there's hardly a point because its devastatingly erroneous.
My question is, how would you save many new objects in a form to one pre-existing parent object?
I have looked extensively at Ryan Bate's work, and many many other blogs and SO posts regarding this. Nothing seems to really point at specifically creating new child objects for one pre-existing parent object.
Update:
I am under the impression that I have to toggle the parent_object's controller actions for def update.
elsif params[:parent_object][:child_object]
#child_object = Child_Object.new(params[:child_object])
if #child_object.valid? && #parent_object.referrals << #child_object
redirect_to new_parent_object_child_object_path(#parent_object)
else
render :action => :new
end
In debugger, if I I place a debugger at the root of def update, and I write :
>> params[:parent_object]
#=> nil
Interesting! That means that when child_object is send to parent_object controller, the params are not filled out for it. Haha, no idea what to do about it though..
Unfortunately that code doesn't work, it was just my attempt at getting closer. ;)
OK, let's give it another shot. Code taken from RB's screencast with replaced object names:
<% form_for #parent_object do |f| %>
<%= f.error_messages %>
<!-- some field of parent object here -->
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :child_objects do |builder| %>
<!-- some fields for child objects -->
<p>
<%= builder.label :content, "Some content for child object" %><br />
<%= builder.text_area :content, :rows => 3 %>
<%= builder.check_box :_destroy %>
<%= builder.label :_destroy, "Remove child object" %>
</p>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
This is a form for #parent_object that has fields for :child_objects. Of course, you've to replace fields with your own.
To make this work, you'll have to build child objects in the constructor:
def new
#parent_object = ParentObject.new
3.times { #parent_object.child_objects.build }
end
Similarly in the edit method, you'd do:
def edit
#parent_object = ParentObject.find(params[:id])
3.times { #parent_object.child_objects.build }
end
To make it work, you need to define the nested attributes for child object:
class ParentObject < ActiveRecord::Base
has_many :child_objects, :dependent => :destroy
accepts_nested_attributes_for :child_objects
end
Hope this helps - this is exactly what RB proposes in his screencasts. Let me know in the comments if you need some further explanation.
-- EDIT --
The update method in the parent_object_controller.rb is just a standard one:
def update
#parent_object = ParentObject.find(params[:id])
if #parent_object.update_attributes(params[:parent_object])
flash[:notice] = "Successfully updated parent object."
redirect_to #parent_object
else
render :action => 'edit'
end
end
But thanks to the accepts_nested_attributes_for in the ParentObject, the nested instances will be created as well.
I didn't include all the model and controller code in this response. You can see the rest of the code by downloading source code for this episode from github.
You can take a look at this answer I gave to a similar question. There're two options: with separate forms, or with a single form.
You'll just have to change the moderate_names_path to the correct path to your parent model instance (and of course the set of fields you want to modify). You can do it with polymorphic_path:
polymorphic_path([#parent_object, #child_object])

Delete multiple objects in Rails with a RESTful controller?

I'd like to delete multiple objects of the same type using a RESTful controller.
The most simple thing I can think of is to have the destroy action expect a comma-separated list of ids of objects to destroy.
Is there a more elegant way to do this?
I think it would be more elegant to take an array of ids:
http://guides.rubyonrails.org/action_controller_overview.html#hash-and-array-parameters
You could use nested forms for it..
See http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes
I think thats the most elegant version...
<% form_for #person do |person_form| %>
<%= person_form.label :name %>
<%= person_form.text_field :name %>
<% person_form.fields_for :children do |child_form| %>
<%= child_form.label :name %>
<%= child_form.text_field :name %>
<% unless child_form.object.new_record? %>
<%= child_form.check_box '_delete' %>
<%= child_form.label '_delete', 'Remove' %>
<% end %>
<% end %>
<%= submit_tag %>
<% end %>
Here's how the RESTful request might look.
POST /posts/delete_multiple HTTP/1.1
Host: www.example.com
post_ids[]=33&post_ids[]=47&post_ids[]=88
Note that while GET, PUT, and DELETE have very specific meanings in the context of REST, POST is more vague and essentially means to take some action. The action to take is given in the URL and additional data specific to the action are passed in the entity (body) of the request. Only use POST in this manner when GET, PUT, and DELETE do not have the intended meaning.
POST is commonly interpreted as "create", but this is not really correct. We commonly use POST for creating new resources when the client doesn't know what the URL of the newly created resource should be. But when the client does get to determine the URL of the newly created resource, the correct verb would be PUT.

Resources