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

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.

Related

Form Design: How to best display/add different statuses?

I am working on a rails form. Essentially, a person can have multiple statuses and switch between the different statuses. In database table, the display will be simple as follows:
status start_date end_date
work 1/1/15 1/10/15
sick 1/11/15 2/15/15
work 2/16/15 3/15/15
sick 1/15/15 1/14/15
I need to prompt user to input these information. I have made a status class which belongs to a person class. So basically, these fields will be a part of nested forms.
My question is: How can I dynamically display these information to make forms elegant and clean to use?
Thanks!
If I understood your domain, your Person has many Status, right?
The simplest way to do it is use the gem cocoon. Your view will look like this:
<%= form_for #person do |person_form| %>
<%= person_form.input :name %>
<%= person_form.fields_for :statuses do |status_form| %>
<%= status_form.field :start_date, :end_date %>
<!-- cocoon's method to dynamically add nested forms -->
<%= link_to_add_association 'add status', person_form, :statuses
<% end %>
<% end %>

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 %>

Mentioning user username in comments (#mention) - Rails

In my rails app, if a user mentions another username in a comment by using the # character, such as #max i'm trying to add autocomplete to suggest a list of users and then automatically create a link_to (username, user_path(user)
This is what I have in my comment partial:
<%= form_for [commentable, Comment.new] do |f| %>
<%= hidden_field_tag :commentable_type, commentable.class.to_s %>
<%= hidden_field_tag :commentable_id, commentable.id %>
<p>
<%= f.text_area :body %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
I'm trying to use this gem: https://github.com/ichord/jquery-atwho-rails
It says to bind the text area with
data = ['tom','john'];
$('textarea').atwho({at:"#", 'data':data});
Where do I actually put this? Can I do something like data = User.all? Should I just be using a regular expression to do this?
I think the reason that data = User.all isn't working is because User.all will return an array of User objects. What you want to do is retrieve those User object usernames (or whatever you want the autocomplete to use, and store that in data instead.
You might try something like
#usernames = User.pluck(:username)
to get all the usernames. Then, in your partial:
data = <% #usernames &>
$('textarea').atwho({at:"#", 'data':data});
This is assuming of course that your partial is an .erb file where you can embed ruby code.
You can do something like this:
<script>
data = <%= raw User.pluck(:username).compact.to_json %>;
$('textarea').atwho({at:"#", 'data':data});
</script>
You might want to move the loading of the usernames into the controller or a helper method. The whole sniplet might belong into an view partial to keep things organized. And it might not be the best idea to load all usernames into the view when there are too many users in the database.

Rails form for mailing

I'm trying to build a form which preloads content from two models (two variables being passed, being shown in the textfields) and then, not saves the data but sends the altered content (from the textfields) as two(?) variables to a mailer class.
I've managed to preload the data from one of the two models but am not sure how the form_for tag has to look like to get both models loaded as well as targeting the mailer class method instead of updating the model entity when pressing "send".
Do I need the accepts_nested_attributes_for attribute inside the model if I'm not saving anything?
I hope someone could give me an small example of the crucial parts. A thousand thanks!
You can use fields_for to include other models in same form. You can use it inside the same form_for what is present.
Checkout the example here from the api docs,
<%= form_for #person do |person_form| %>
First name: <%= person_form.text_field :first_name %>
Last name : <%= person_form.text_field :last_name %>
<%= fields_for #person.permission do |permission_fields| %>
Admin? : <%= permission_fields.check_box :admin %>
<% end %>
<%= f.submit %>
<% end %>
when you submit the data from this form, you can just use that data to pass to the mailer class from controller. UserMailer.get_user_info(params[:name], params[:address]).send
Creates a scope around a specific model object like #form_for, but doesn't create the form tags themselves. This makes #fields_for suitable for specifying additional model objects in the same form.
Refer Docs here:.
fields_for(record_name, record_object = nil, options = {}, &block)

Difference between form_for , form_tag?

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.

Resources