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?
Related
I am using the cocoon gem to try and achieve adding an object which belongs to another with nested fields. I have a 'user_resolution' which has many 'milestones'. I have set up the associations accordingly in both of these models. For some reason, milestones are failing to be created, however if I add one manually in the database I can successfully update it. I am able to dynamically add the fields and remove them using the cocoon gem but that is all. When I click 'add milestone' it redirects me to the show view of the user resolution and throws the success message saying user resolution has been updated, no errors are thrown but the milestone(s) is/are not created.
user_resolution.rb
has_many :milestones
accepts_nested_attributes_for :milestones, reject_if: :all_blank, allow_destroy: true
milestone.rb
belongs_to :user_resolution
I have set up the nested form within the edit view as for now I only want users to add a milestone to a resolution in the edit view.
user_resolutions/edit.html.erb
<%= form_for(#user_resolution) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<%= f.fields_for :milestones do |milestone| %>
<%= render 'milestone_fields', f: milestone %>
<% end %>
<%= link_to_add_association 'Add Milestone', f, :milestones %>
<%= f.submit "Add Milestone" %>
<% end %>
_milestone_fields.html.erb
<div class="nested-fields">
<div class="field-row">
<%= f.label :name, 'Name' %>
<%= f.text_field :name %>
</div>
<div class="field-row">
<%= f.label :description, 'Name' %>
<%= f.text_area :description %>
</div>
<div class="field-row">
<%= f.label :severity, 'severity' %>
<%= f.check_box :severity %>
</div>
<div class="field-row">
<%= f.label :target_date, 'target_date' %>
<%= f.date_select :target_date %>
</div>
<%= link_to_remove_association 'Remove', f %>
</div>
The permitted parameters within the user resolutions controller also contain the following
milestones_attributes: [:id, :user_resolution_id, :name, :description, :target_date, :severity, :complete, :_destroy]
The milestones themselves have no views, they only have a model and a controller. The controller create action (which i'm unsure is required for nested forms) contains the standard following code
def create
#milestone = Milestone.new(milestone_params)
if #milestone.save
redirect_to user_resolutions_path,
:flash => { :success => "You successfully created a milestone" }
else
redirect_to new_milestone_path,
:flash => { :error => "Oops something went wrong. Try again." }
end
end
I've been as informative as I can but if you need anything else let me know. Thanks guys.
which i'm unsure is required for nested forms
You don't need a create action for milestones - they'll be populated from the user_resolutions#create controller action.
There are several things to look at with this. I'll detail some here. This won't be a specific answer, but may help point you in the right direction.
Firstly, you need to make sure you're receiving the correct params.
Cocoon does a great job building the nested form - you need to make sure it's obliging Rails' nested attribute structure.
To do this, you should right-click > view source.
In the f.fields_for section (it won't be called that in the HTML), you'll be looking for the equivalent to the following:
<input type="text" name="milestones_attributes[0][name]" value="">
The important thing to note is the name...
Each time you use a form, or any Rails view helper for that matter, you're really just building standard HTML. form_for just creates an HTML form, and thus any params contained within it need to adhere to a certain structure for Rails to recognize the params.
The f.fields_for elements will typically be called x_attributes[:id][:param] - this is passed to Rails, which cycles through each [:id] to determine the number of nested params to add.
You need to check the source for the above naming structure. If you see it, that's good. If not, it means you haven't built your form properly.
Secondly, you need to make sure your objects are being built in the controller.
I'm not sure how Cocoon does this, but essentially, each time you use f.fields_for, you have to build the associated object before:
def new
#user_reservation = UserReservation.new
#user_reservation.milestones.build #-> this is what makes f.fields_for work
end
If the first step shows incorrect element naming, it means your associative objects are not being built (which is why they're not being recognized).
To test it, you should build the associative objects in the new method, before sending.
Finally, you'll want to post your params.
These tell you in explicit detail what Rails is doing with the nested attributes, allowing you to determine what's happening with them.
Sorry for the long-winded answer. You'll not have received any answers anyway, so I felt it prudent to give you something.
I have a Task model, which is joined in a many-to-many relationship with an Objective model.
I have a Task edit form where a user can associate any Objective with a Task via checkboxes. When a checkbox is checked, it should indicate an association between the Task and a particular Objective; when a checkbox is not checked, there should be no association. This should be persisted to the database when the form submits.
<%= form_for #task do |f| %>
<% Objective.all.each do |objective| %>
<%= check_box_tag :objective_ids, objective.id, #task.objectives.include?(objective), :name => 'task[objective_ids][]' %>
<% end %>
<%= f.button :submit %>
<% end %>
Updating a Task seems to work absolutely fine as long as one checkbox is checked by the user, but when the user does not check any checkboxes, the :objective_ids param (which is an array of Objective ids) is not included in the POST action at all. Because of this, when I do #task.update_attributes(params[:task]) in the controller, the Task's collection of Objectives is not updated (i.e. the Task should no longer have any Objectives associated with it, because no checkboxes were checked).
So how can I ensure the :objective_ids param is included in the POST, even if only as an empty array?
Add hidden field above all checkboxes with empty value. It will be sent in case user didn't check any checkboxes.
<%= form_for #task do |f| %>
<%= hidden_field_tag "task[objective_ids][]", nil %>
<% Objective.all.each do |objective| %>
<%= check_box_tag :objective_ids, objective.id, #task.objectives.include?(objective), :name => 'task[objective_ids][]' %>
<% end %>
<%= f.button :submit %>
<% end %>
Here is a good railscasts about this.
You may also want to check the source code for it.
This isn't working as I'd like.
I have an entry form for a new job, which is made up of a number of steps. By default there are 4 new and unsaved steps populated per job.
Using the code from the view below, I am able to attach the jQuery UI datepicker control to the text fields and they appear against the correct input field when i click them.
However, when I select a date from the picker it is only ever going into the field representing the start date of the first step.
<%= form_for #job, :url => jobs_path do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<% #job.steps.each do |step| %>
<%= fields_for "job[step_attributes][]", step do |s| %>
<%= s.label :name %>
<%= s.text_field :name %>
<%= s.label :start_date %>
<%= s.text_field :start_date, :class => :datepicker %>
<%= end %>
<% end %>
<script type="text/javascript">
$(function() {
$('.datepicker').datepicker({ dateFormat: "dd/mm/yy"});
});
</script>
I know ultimately this has to do with the generated id attribute for the input elements being the same, was wondering, has anybody successfully overcome this issue?
This fiddle: http://jsfiddle.net/twilson/u9m9L/ demonstrates my problem.
First, invoke fields_for on your form builder instead:
<%= f.fields_for :steps do |s| %>
Second, you don't need to iterate through #job.steps if you specify the association name to fields_for. There's an example on how to use it with one-to-many assoiciations here.
If you still have this problem, paste your generated HTML, it would easier to find the cause.
EDIT
OK so the problem is because Rails form builder does not generate child indexes to give unique IDs to nested fieldsets. This most likely happened because the associations are built but not yet saved to the DB. One way I see is to assign child indexes manually, like so:
<% #job.steps.each_with_index do |step,i| %>
<%= f.fields_for :steps, step, :child_index => i do |s| %>
See if that helps.
This is a somewhat contrived scenario given that you have only new objects. To be able to dynamically add/remove nested items, this would be a bit trickier. You can see how this can be dealt with in the nested model Railscasts.
I want to be able to fill a table with some data through AJAX. My problem is that this data is from two different models. This will be long but please bear with me.
Imagine an application that would fill the plan for working out at the gym (clearly not an application for me :D). This plan has many routines (per type basis or day basis) which has many steps to go through.
Is it possible to use AJAX to fill a nice table that fills dynamically? Ideally I would prefer to save and display a table each time the user fills any new data.
What I have so far:
In the models I added the accepts_nested_attributes_for property.
I am using nested_form that allows us to add and remove nested models.
Let's take a look at our form:
app/views/plans/_form.html.erb
<%= nested_form_for #plan, :url => plan_path(#plan), :html => { :class => :form } do |f| %>
<%= f.label :name, "Plan Name" %>
<%= f.text_field :name %>
<%= f.fields_for :routines do |r| %>
<%= render 'routine_fields', :f => r %>
<% end %>
<%= f.submit %>
<%= f.link_to_add "Add a routine", :routines %>
<% end %>
So fields_for allows us to save many routines inside a plan, NICE! Let's define our fields views:
app/views/plans/_routine_fields.html.erb
<%= f.label :name, "Routine Name" %>
<%= f.text_field :name %>
<%= f.fields_for :steps do |s| %>
<%= render 'step_fields', :f => s %>
<% end %>
<%= f.link_to_add "Add a step", :steps %>
<%= f.link_to_remove "Remove this routine" %>
app/views/plans/_step_fields.html.erb
<%= f.label :name, "Step Name" %>
<%= f.text_field :name %>
<%= f.link_to_remove "Remove this step" %>
This works great! We can add as many routines inside a plan and many steps inside a routine we are able to create a complete plan in one view.
BUT IS UGLY! and also very confusing! So my problem again: How would I update the table each time the user fills any new data?
Ideal:
One approach that you could use for this would be to break up the form into different forms that are loaded via ajax, and just continuously update attributes on a model. You could also just use the form that you have now and break up the html so that it doesn't all show in a table.
For dealing with forms, you could check out the railscast on wicked
For dealing with ajax, there is a very good railscast on ajax/jquery
You could also break up this logic into different partials, and change the UX a bit, so that a user still has the same functionality, but it lives on a different view. You can add ajax to this by following the previously mentioned ajax railscast or using the turbolinks gem (though turbolinks is a bit different).
It seems to me that this is primarily a UX problem, and you're trying to crowbar a particular implementation into a rigid UX. I would try reframing the UX to see if there is a more elegant solution to the problems you have mentioned.
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