13

I have two models, Developers and Tasks,

class Developer < ActiveRecord::Base
  attr_accessible :address, :comment, :email, :name, :nit, :phone, :web
  has_many :assignments
  has_many :tasks, :through => :assignments
end

class Task < ActiveRecord::Base
  attr_accessible :description, :name, :sprint_id, :developer_ids
  has_many :assignments
  has_many :developers, :through => :assignments
end

class Assignment < ActiveRecord::Base
  attr_accessible :accomplished_time, :developer_id, :estimated_time, :status, :task_id
  belongs_to :task
  belongs_to :developer
end

im taking care of the relation by adding an Assignment table, so i can add many developers to one task in particular, now i would also like to be able to manipulate the other fields i added to the joining table like the 'estimated_time', 'accomplished_time'... etc... what i got on my Simple_form is `

<%= simple_form_for [@sprint,@task], :html => { :class => 'form-horizontal' } do |f| %>
  <%= f.input :name %>
  <%= f.input :description %>
  <%= f.association :developers, :as => :check_boxes %>

  <div class="form-actions">
    <%= f.button :submit, :class => 'btn-primary' %>
    <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
                project_sprint_path(@sprint.project_id,@sprint), :class => 'btn' %>
  </div>
<% end %>`

This only allows me to select the developers, i want to be able to modify the estimated_time field right there.

Any Suggestions?

Gaspar Tovar
  • 145
  • 1
  • 1
  • 6

3 Answers3

30

I love how simple-form has the association helpers, making it really easy in some cases. Unfortunately, what you want you cannot solve with just simple-form.

You will have to create assignments for this to work.

There are two possible approaches.

For both you will have to add the following to your model:

class Task
  accepts_nested_attributes_for :assignments
end

Note that if you are using attr_accesible, you should also add assignments_attributes to it.

The easy approach

Suppose you know how many assignments, maximally, a task would have. Suppose 1 for simplicity.

In your controller, write

def new
  @task = Task.build
  @task.assignments.build
end

This will make sure there is one new assignment.

In your view write:

= simple_form_for [@sprint,@task], :html => { :class => 'form-horizontal' } do |f| 
  = f.input :name 
  = f.input :description 

  = f.simple_fields_for :assignments do |assignment|
    = assignment.association :developer, :as => :select
    = assignment.estimated_time

  .form-actions
    = f.button :submit, :class => 'btn-primary'
    = link_to t('.cancel', :default => t("helpers.links.cancel")),
                project_sprint_path(@sprint.project_id,@sprint), :class => 'btn'

The problem with this approach: what if you want more than 1, 2 or 3?

Use cocoon

Cocoon is a gem that allows you to create dynamic nested forms.

Your view would become:

= simple_form_for [@sprint,@task], :html => { :class => 'form-horizontal' } do |f| 
  = f.input :name 
  = f.input :description 

  = f.simple_fields_for :assignments do |assignment|
    = render `assignment_fields`, :f => assignment
  .links
    = link_to_add_association 'add assignment', f, :assignments

  .form-actions
    = f.button :submit, :class => 'btn-primary'
    = link_to t('.cancel', :default => t("helpers.links.cancel")),
                project_sprint_path(@sprint.project_id,@sprint), :class => 'btn'

And define a partial _assignment_fields.html.haml :

.nested_fields
  = f.association :developer, :as => :select
  = f.estimated_time
  = link_to_remove_association 'remove assignment', f

Hope this helps.

Kris Khaira
  • 132
  • 11
nathanvda
  • 49,707
  • 13
  • 117
  • 139
  • Hi Nathan, im giving it a try using cocoon, but i dont see what should i change in my model or maybe in my controller, because its not recognizing the assignment. here is the error C:/Users/Gaspar/Desktop/vkanban2/app/views/tasks/_assignment_fields.html.erb where line #1 raised: undefined local variable or method `assignment' for #<#:0x590cd90> Extracted source (around line #1): 1: <%= assignment.association :developer, :as => :select %> 2: <%= assignment.estimated_time%> 3: <%= link_to_remove_association 'remove assignment', f%> – Gaspar Tovar Oct 24 '12 at 18:52
  • Ow yes, cut and paste error. This code is of course untested. The error states, obviously, that the variable `assignment` is not known in the partial, and indeed, in the render I pass a variable called `f`. So you should write `f.association` instead of `assignment.association`. I edited my answer accordingly. – nathanvda Oct 24 '12 at 19:26
  • im guessing instead of assignment.association it should be f.association however this is still not recognizing the variable – Gaspar Tovar Oct 24 '12 at 19:31
  • In your model you need to add the `accepts_nested_attributes_for :assignments` for it to work correctly. Also make sure the cocoon.js is included last in the list of javascripts. – nathanvda Oct 26 '12 at 23:34
  • Why don't you or the cocoon github page include "<%", "<%=", or "%>"? – michaelsnowden Feb 09 '15 at 06:38
  • I use haml instead of erb, which is imho cleaner and more readable and easier to maintain. You should definitely check out haml or slim instead of erb for view rendering. On the cocoon wiki we have the examples in erb as well: https://github.com/nathanvda/cocoon/wiki/ERB-examples – nathanvda Feb 09 '15 at 09:16
  • What about strong parameters in Rails 4? How to set them up? – Qaiser Wali Jun 10 '15 at 09:15
  • The nested attributes are stored inside a key `_attributes`. You can easily verify this in your log-file to see what is actually posted to the controller. So when you have a project and nested tasks, you will need something like: `params.require(:project).permit(:name, :description, tasks_attributes: [:id, :description, :done, :_destroy])` – nathanvda Jun 10 '15 at 11:43
  • Simple_form ERB examples would be useful too. – David Sigley Sep 25 '15 at 11:01
  • @DavidSigley you are always welcome to add those. Personally it seems like a trivial difference (drop `f.inputs` and replace `f.semantic_fields_for` by `f.simple_fields_for`). I would rather suggest you try to understand haml, it is really easy, and it is imho much clearer. If you currently are having troubles with a nested form using simple-form and erb, ask a new question. Also: your comment felt _very_ passive-agressive to me. If that was not intended, you should work on that. Is that a question? A statement? A request? Do you expect me to jump up and fix it? – nathanvda Sep 25 '15 at 12:06
  • @nathanvda - It was a suggestion. I think the _tone_ in which you read the comment was in your reading and not my writing. Either way, I have added it to the docs. Thanks. – David Sigley Sep 25 '15 at 12:17
4

The thing is that by using this:

<%= f.association :developers, :as => :check_boxes %>

You're actually only setting the developer_ids attribute, which will automatically build the assignments for you as it's a has many :through. For this I believe you should probably be using nested attributes, for each of the assignments, and each record would have a select box or similar to choose the related developer for that particular assignment in this task. It's quite similar to what Cojones has answered, but you should not be using check boxes for this association, since you're going to be dealing with a single assignment which contains a single developer. And with nested attributes, you should be able to create as many assignments you want.

That I believe is the easiest way to start with.

Carlos Antonio
  • 541
  • 4
  • 5
3

I think it should look somehow like this:

= f.simple_fields_for :assignments do |fa|
  = fa.association :developer, as: :check_boxes
  = fa.input :estimated_time
  ...
Cojones
  • 2,930
  • 4
  • 29
  • 41
  • 1
    its not working, its returning an error Association cannot be used in forms not associated with an object Extracted source (around line #6): 3: <%= f.input :description %> 4: <%= f.input :stimated_time %> 5: <%= f.simple_fields_for :assignments do |fa|%> 6: <%= fa.association :developers, as: :check_boxes%> 7: <%= fa.input :estimated_time%> 8: <%end%> 9: <%= f.association :developers, :as => :check_boxes %> – Gaspar Tovar Oct 15 '12 at 01:01
  • 1
    The association should be `:developer` (singular), so `=fa.association :developer, :as => ...` – nathanvda Oct 15 '12 at 21:59
  • but stillthis is not what i was looking for – Gaspar Tovar Oct 24 '12 at 19:14