I'll try and explain this as much and as easily as possible.
I have a Rails form, and 3 models.
Models: DemoModule, SalesDemo, and SalesDemoModule
What I want to do in my view/form is create a new SalesDemo, but a SalesDemo has many SalesDemoModules.
In the controller I have:
#sales_demo = SalesDemo.new
#demo_modules = DemoModule.find(:all, :conditions => ['active = true'])
How can I, in my view, have a text field row for each DemoModule, which I can pass back to the controller action, to save into SalesDemoModule?
You can specify that the SalesDemo accepts_nested_attributes_for SalesDemoModule, which then allows you to created a nested form (i.e. within a form_for a SalesDemo, you can have fields_for SalesDemoModule). Here's a simple example.
Simply put:
<%= form_for #sales_demo do |sales_demo_form| %>
<%= sales_demo_form.text_field "some_sales_demo_property" %>
<%= sales_demo_form.fields_for #demo_modules do |modules| %>
<%= modules.text_field "some_module_text_field" %>
<% end %>
<% end %>
In the SalesDemo, you will need to have
accepts_nested_attributes_for :demo_modules
You can get some more information here.
Related
I have a User controller that possesses the two following records:
user.vehicles from VehiclesController
user.trip_distances from TripDistancesController
In the Vehicles view, I need to display and update both models (Vehicle and TripDistance)
I figured out that I need to use a nested form. However, I don't understand how to implement it since my #trip_distances record needs a loop in order to display all of its content.
First, I tried to put this in the Vehicle model, but since both are NOT linked, I wonder if this is correct:
accepts_nested_attributes_for :trip_distances, allow_destroy: true
Then, in the Vehicle view's _form file, during the edit action, I have:
<%= form_for(#vehicle) do |f| %>
I need to put my nested form inside it. Here's what I got so far:
<% #trip_distances.each do |t| %>
<%= f.fields_for t do |fields| %>
<div class="field">
<%= fields.text_field :id_contract %>
<%= fields.number_field :length %>
</div>
<% end %>
<% end -%>
My trip_distances records are correctly displayed, but the unique Submit button doesn't work anymore. I'd like to update both model while clicking on one button. What am I missing?
I was able to make it work by associating the two models. Then I use the following fields_for tag:
<%= f.fields_for :trip_distances do |builder| %>
My submit button is now working, but the :trip_distances fields are not updated, I don't know why.
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.
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 %>
Sometimes we need form without model creation - for example search field or email, where should be send some instructions. What is the best way to create this forms? Can i create virtual model or something like this? I'd like to use formtastic, but not form_tag.
Firstly, Formtastic doesn't need a model in all cases, although it certainly works best and requires less code with a model.
Just like Rails' own built-in form_for, you can pass in a symbol instead of an object as the first argument, and Formtastic will build the form and post the params based on the symbol. Eg:
<% semantic_form_for(:session) do |f| %>
...
<% end %>
This will make the form values available to your controller as params[:session].
Secondly, a model doesn't mean an ActiveRecord model. What I mean is, Formtastic will work with any instance of a class that quacks like an ActiveRecord model.
A classic example of this that many people are using Authlogic for authentication with Formtastic. Part of Authlogic is the idea of a UserSession model, which works fine:
Controller:
def index
#user_session = UserSession.new
end
Form:
<% semantic_form_for(#user_session) do |f| %>
<%= f.input :login %>
<%= f.input :password %>
<% end %>
This will make your form data available in your controller as params[:user_session].
It's really not that hard to create a model instance to wrap up the concerns of your model. Just keep implementing the methods Formtastic is expecting until you get it working!
default_language.rb
class DefaultLanguage
attr_accessor :language_id
end
foo_controller.rb
def index
#default_language = params[:default_language] || Language.find_by_name("English")
end
index.erb
<% semantic_form_for #default_language do |form| %>
<% form.inputs :id => 'default_language' do %>
<%= form.input :id,
:as => :select,
:collection => #languages,
:required => false,
:label => "Primary Language:",
:include_blank => false %>
<% end %>
<% end %>
I used AJAX to post the form when the value changed.
Or you simply create a form with form_for and leave the model reference blank.
for example
<% form_for "", :url=>some_url do |f| %>
<%= f.text_field "some_attribute" %>
<%= submit_tag "submit" %>
You can fetch the values by simply saying params[:some_attribute] in your controller.
What is the difference between form_for and form_tag? Is anything different for form_remote_for and form_remote_tag?
You would use form_for for a specific model,
<% form_for #person do |f| %> # you can use f here
First name: <%= f.text_field :first_name %>
Last name : <%= f.text_field :last_name %>
<% end %>
Form_tag create basic form,
<%= form_tag '/person' do -%>
<%= text_field_tag "person", "first_name" %>
<% end -%>
form_for prefers, as its first arg, an activerecord object; it allows to easily make a create or edit form (to use it in a "new" view you should create an empty instance in controller, like:
def new
#foo = Foo.new
end
It also passes a form variable to the block, so that you don't have to repeat the model name within the form itself. it's the preferred way to write a model related form.
form_tag just creates a form tag (and of course silently prepare an antiforgery hidden field, like form_for); it's best used for non-model forms (I actually only use it for simple search forms or the like).
Similarly, form_remote_for and form_remote_tag are suited for model related forms and not model related forms respectively but, instead of ending in a standard http method (GET, POST...), they call an ajax method.
All this and far more are available for you to enjoy in the FormHelper and PrototypeHelper reference pages.
EDIT 2012-07-13
Prototype has been removed from rails long ago, and remote forms have completely changed. Please refer to the first link, with reguard to the :remote option of both form_for and form_tag.
These should be similar:
<% form_for #person do |f| %>
<%= f.text_field :name %>
<% end %>
and:
<%= form_tag '/person' do %>
<%= text_field_tag "person[name]" %>
<% end %>
If you want to submit the same params to the controller, you would have to define this explicitly.