passing dynamic params field - ruby-on-rails

Im working with Rails 3.0.3
I want to create bills in my App. Each Bill has many entries (Material, how much of that and the Price)
The Problem i have, is that i want to write the bill and the entries and then save both at the same time. So when you click on save Bill, the Bill + each Entry should be created (saved in the db).
I can write the bill + each entry (with javascript), but i dont know how i could save both of them. Right now i can only save the bill it selft. Is it possible to pass a dynamic field via params so i can handle that in the bills controller to save? How would you implement this?

What you are looking for is called nested form, you have a main form for your bill and multiple forms that are dynamically generated as children of this general form using fields_for like this:
<% form_for #bill do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :entry do |builder| %>
<%= render "entry", :f => builder %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
Of course you will need some js for the dynamic creation of the different entries, here you have a couple of railscasts that will be helpfull.
Nested model form Part 1
Nested model form Part 2

Related

Rails multi-record form only saves parameters for last record

I'm trying to offer teachers a form that will create multiple students at once. It seems that most people tackle this concept with nested attributes, but I'm having a hard time understanding how that would work when I'm only using a single model. This article made it seem possible to achieve this without nested attributes, but my results are not working the way the author suggests. The students array should include one hash for each section of the form. But when I submit the form and check the parameters, only one single hash exists in the array.
Adjusting her approach, I've got this controller:
students_controller.rb
def multi
#student_group = []
5.times do
#student_group << Student.new
end
end
(I'm using an action I've called "multi" because it's a different view than the regular "create" action, which only creates one student at a time. I've tried moving everything into the regular create action, but I get the same results.)
The view:
multi.html.erb
<%= form_tag students_path do %>
<% #student_group.each do |student| %>
<%= fields_for 'students[]', student do |s| %>
<div class="field">
<%= s.label :first_name %><br>
<%= s.text_field :first_name %>
</div>
<div class="field">
<%= s.label :last_name %><br>
<%= s.text_field :last_name %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= submit_tag %>
</div>
<% end %>
The results:
(byebug) params
<ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"3Xpi4XeqXuPs9jQvevy+nvGB1HiProddZzWq6Ed7Oljr3TR2fhx9Js6fN/F9xYcpgfDckCBOC2CoN+MrlFU0Bg==", "students"=>{"first_name"=>"fff", "last_name"=>"ggg"}, "commit"=>"Save changes", "controller"=>"students", "action"=>"create"} permitted: false>
Only one has is included for a student named "fff ggg". There should be four other hashes with different students.
Thank you in advance for any insight.
fields_for is only used in conjunction with form_for. The for is referring to a model, which it expects you to use. Since you're trying to build a form with no model, you have to construct your own input field names.
Don't use fields_for but instead, render each input using the form tag helpers e.g.
<%= label_tag "students__first_name", "First Name" %>
<%= text_field_tag "students[][first_name]" %>
...and so on.
The key is that the field names have that [] in them to indicate that the students parameters will be an array of hashes. You almost got it by telling fields_for to be called students[] but fields_for ignored it because it needs a model to work correctly.

Ruby on Rails: assign relationship on creation

I'm new to Ruby on Rails. There are two models in my project: room and guest. The association is "room has_many guests" and "guest belongs to room".
I have separated views for manage rooms and guests. Rooms don't require "guests" value on creation. However, I want to create new guests and assign it to certain room at the same time. What will be the proper way to do it? How do I transfer the input from web and match the entities in database.
The code is pretty much the same as "Getting Started with Rails". In the tutorial, they add "comments" in the "article" view and use "comment" as a sub-resource of "article". In my case, I treat the two models equally and want to manage them in separated views.
Update:
I used the collection_select and try to work with my guest_controller.
<%= form_for :guest, url: guests_path do |f| %>
<% if #guest.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#guest.errors.count, "error") %> prohibited this guest from being added:
</h2>
<ul>
<% #guest.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :phone %><br>
<%= f.text_field :phone %>
</p>
<p>
<%= f.label :room%><br>
<%= f.text_field :room %>
</p>
<p>
<%= f.label :room %><br>
<%= f.collection_select(:room_id, Room.all, :id, :title) %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', guests_path %>
In my guest_controller, the create method called by the form above is :
def create
#guest = Guest.new(guest_params)
#guest.room = Room.find(params[:room_id])
if #guest.save
redirect_to #guest
else
render 'new'
end
end
However, when I create a new guest, it shows that:
ActiveRecord::RecordNotFound in GuestsController#create
Couldn't find Room with 'id'=
I checked that room_id=4 and Room.find(4) return the proper room.
What's wrong?
If you want to select one room from those that exist, use collection_select form helper, here is a relevant snippet from the docs:
f.collection_select(:city_id, City.all, :id, :name)
This outputs a dropdown list that:
fills in city_id parameter in this context
uses City.all for filling in the options in the list (I will be referring to "each" city as city)
uses city.id as data (that gets sent in the form)
shows city.name for each city in the dropdown list (hopefully, human-readable)
Bear in mind though, that in terms of security it's like "look, you can select this, and this and this!", that does not prevent users from selecting an unlisted option: either by modifying form markup by hand or sending handcrafted queries.
So should you ever be limiting access to specific rooms, and list only Room.unlocked (unlocked assumed a scope), make sure the received room_id refers to a room from that scope as well. Most of these problems are dealt with using either validations or careful association management (Room.unlocked.find_by_id(:room_id) that outputs nil if the room is not in that scope).
UPD: as for the latest problem you're having -- your understanding on how the form contents look in params seems to be wrong. It's quite a common misconception actually.
form_for :guest will construct a separate object/hash in params[:guest], with all the form's fields inside it. So it actually is inside params[:guest][:room_id], but no, don't rush with adding the missing part.
You've already built a #guest object from entire params[:guest], so if the room actually exists, it's inside #guest.room already and can be validated inside the model during save. Have a look at Rails validators.
Take a look at the fields_for tag:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
It allows just that, to create a guest while creating a room and associating each other.

How to dynamically add a (HABTM) collection_select + associated text_field in a Rails form?

For a personal invoicing app in rails I could use some advice on the following: In a new invoice form, how to dynamically add a new row consisting of a collection_select for products and an associated text_field for amount by clicking add new product?
Since one invoice has many products and a product has many invoices, I figured this is a HABTM relationship both ways. Therefore I have created the tables invoices, products, and their join table invoices_products.
I would like the invoices/new page to have a form where a user can use a collection_select to pick a product (all products are already preloaded in the products table) and fill in the amount in a text field behind it.
A user should be able to duplicate these two fields (collection_select and text_field) by clicking add new product, something like RailsCast #403 Dynamic Forms 04:50.
Now how would I go about doing that? And in what table would I put the data from the amount text_field, since there are multiple products per invoice so multiple amounts?
Any answer in the right direction will be greatly appreciated!
This depends on how many additional fields you want
A good tutorial for this can be found here
Ajax
The right way would be to add the element dynamically with Ajax
This is tricky because it means you're not going to have the form_builder object available for the dynamically added items. Regardless, you can still do it with this code:
#config/routes.rb
get "new_field_path", to: "invoices#new_item"
#app/views/controller/index.html.erb
<%= ... form %>
<%= link_to "Add Field", new_field_path, remote: :true %>
Controller
#app/controllers/invoices_controller.rb
def new_item
#invoice = Invoice.new
#invoice.products.build
render "new_item", layout: false
end
#app/views/invoices/new_item.html.erb
<%= form_for #invoice do |f| %>
<%= render partial: "products_fields", locals: { f: f } %>
<% end %>
#app/views/invoices/_products_fields.html.erb
<%= f.fields_for :products, child_index: Time.now.to_i do |p| %>
<%= p.text_field :name %>
<% end %>
Form
#app/views/invoices/new.html.erb
<%= form_for #invoice do |f| %>
<%= render partial: "products_fields", locals: { f: f } %>
<%= link_to "Add Field", new_field_path, remote: :true %>
<%= f.submit %>
<% end %>

Select or create from view in rails

In rails is there any simple way to implement select or create from view.
Eg:
Product has_many(or has_one) Tags.
While creating new Product I can select existing tags or create new one.
This can be done by using JavaScript and other ways are there.. But all will take more time and effort.
Please share if you know other simple way...
Edit:
Something like this.
But imagine you have 100 tags or more ! your page will look bad with 100 checkbox or more..., one elegant way to do this is by using a jQuery plugin called jQuery Tokeninput i use it in my project and it's very helpful for what do you want, you can find the plugin Here
This is a screencast on how to use it : Token fields
and this is the revised version : Token Fields (revised)
check also this blog post about the same plugin if you want too How to create a token input field where the user can also add new items
cheer
Yep.
You are after nested forms. Try, https://github.com/ryanb/nested_form
For example,
<% form_for #product do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<% f.fields_for :tags do |tag| %>
<p>
<%= tag.label :content, "Tag Name" %><br />
<%= tag.check_box :content %>
</p>
<%= tag.link_to_remove "Remove this tag" %>
<% end %>
<%= tag.link_to_add "Add new tag" %>
<p><%= f.submit "Submit" %></p>
<% end %>
Setup the controller and model as given in the documentation and try the above code in the view. This railscast will help you a lot in figuring nested forms http://railscasts.com/episodes/197-nested-model-form-part-2

Rails: submit child model in nested form

I've researched a while on this topic but haven't found a real solution to what I am trying to achieve.
I'm working with a nested one-to-many nested form (Project has many Tasks). And each task has many attributes, e.g. type, assigned_individual, due_date, etc. I got no problem having the parent Project and the nested child model Tasks saved/updated on one submit. But what I need to achieve seems to be the opposite of this effort. I need one Ajax save call for each Task besides the global submit. so when the task list gets long, users don't have to worry about losing what they have written earlier for the other tasks. They can click that save button and get's a feedback saying that what he has entered for the task has been saved.
Currently the Project is in a form_for wrapper, and the Tasks are in fields_for partial
these are the simplified version of what I have right now.
Here is the form in edit.html.erb
<%= form_for #project do |f| %>
<p>f.text_field :title</p>
<p>f.text_field :author</p>
<%= render :partial=> 'tasks', :collection=> #project.tasks %>
<%= f.submit 'submit' %>
<% end %>
Here is tasks partial:
<%= fields_for :tasks do |f| %>
<label>Category</label>
<%= f.text_field :category%>
<label>Description</label>
<%= f.text_field :description%>
<label>Author</label>
<%= f.text_field :author%>
<label>Assigned Individual</label>
<%= f.text_field :assigned_individual%>
<label>Notes</label>
<%= f.text_area :notes%>
<label>Due Date</label>
<%= f.text_field :due_date%>
<button onclick='update_task();'>Save Task</button>
<% end %>
Since I cannot have multiple submit button in one form, what I can think of right now is to use jQuery to collect every single user entry in that partial and pass them as a big hash back to the Task controller update method.
update_task()
but is there a cleaner way?

Resources