Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple_Form Association with has_many :through extra field

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?

like image 531
Gaspar Tovar Avatar asked Oct 11 '12 05:10

Gaspar Tovar


3 Answers

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.

like image 73
nathanvda Avatar answered Nov 01 '22 18:11

nathanvda


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.

like image 4
Carlos Antonio Avatar answered Nov 01 '22 18:11

Carlos Antonio


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
  ...
like image 3
Cojones Avatar answered Nov 01 '22 17:11

Cojones