Here are my 3 models.
User
    has_many :memberships
    has_many :teams, through: :memberships, dependent: :destroy
    accepts_nested_attributes_for :memberships
Team
    has_many :memberships
    has_many :users, through: :memberships, dependent: :destroy
    accepts_nested_attributes_for :memberships
Membership
    belongs_to :team
    belongs_to :user
Here are some portions of my Team controller. My objective here is to add/update members to a certain team. Note that the source for adding members already exists as a group of users.
TeamsController
    def create
        @team = Team.new(team_params)
        @team.users << User.find(member_ids) #add leader and members to team
        if @team.save 
            #redirect to created team
        else
            #show errors
        end
    end
    def update
        #TO DO: update team roster here
        if @team.update(team_params)
            #redirect to updated team
        else
            #show errors
        end
    end
Strong parameters for Team controller
#parameters for team details
def team_params
    params.require(:team).permit(:name, :department)
end
#parameters for members (includes leader)
def members_params
    params.require(:team).permit(:leader, members:[])
end
#get id values from members_params and store in an array
def member_ids
    members_params.values.flatten
end
For the form, I only have:
I can successfully create a team, together with the passing of validations (both team and membership model), on my create. However, I have no idea on how to update the team, because if I use @team.users.clear and then simply do the same thing from create (I know, it's a bit stupid to do this), it will validate, but it will save it regardless if there's an error or not.
FORM CODE
<%= form_for(@team, remote: true) do |f| %>
    <%= f.label :name, "Name" %>
    <%= f.text_field :name %>
    <%= f.label :department, "Department" %>
    <%= f.select :department, options_for_select(["Architectural", "Interior Design"], department), include_blank: true %>
    <%= f.label :leader, "Leader" %>
    <%= f.select :leader, select_leaders(department, @team.id), {include_blank: true, selected: pre_select(:leader)} %>
    <%= f.label :members, "Members" %>
    <%= f.select :members, select_members(department, @team.id), {include_blank: true}, {id: "team_members", multiple: :multiple,  data: {member_ids: pre_select(:members)}}%>
<% end %>
Note for the form:
:members field is select2 enabled.So my questions here are:
SOME OPTIONS LEADING TO SOLUTION
Option #1 (best solution so far)
I only did a first-aid solution for this, so I think there's a better approach than what I did below. What I did here is to create users params with the users found from the member_ids as values.
TeamsController
    def create
        team = Team.new(team_params.merge({users: User.find(member_ids)}))
        ...
    end
    def update
        ...
        if @team.update(team_params.merge({users: User.find(member_ids)}))
        ..
    end
Option #2
Independent from solution 1, I only had team_params as strong parameter. 
TeamsController
    ...
    private
        def team_params
            params.require(:team).permit(:name, :department, :leader, members:[])
        end   
I created setter methods for both leader and members. But it seems that members overwrites the leader setter because I used the update method for both setters, and the update uses the same resource which is users. A workaround seems to be possible with this option.
Team
    ...
    def leader=(leader_id)
        #self.update(users: User.find(leader_id))
    end
    def members=(members_ids)
        #self.update(users: User.find(members_id))
    end
                Since, leader and members are not so different in your scenario. You can change your models and view form to something like this:
class Team < ActiveRecord::Base
  has_many :memberships
  has_many :users, through: :memberships, dependent: :destroy
  accepts_nested_attributes_for :memberships
end
class User < ActiveRecord::Base
  has_many :memberships
  has_many :teams, through: :memberships, dependent: :destroy
end
class Membership < ActiveRecord::Base
  belongs_to :team
  belongs_to :user
  accepts_nested_attributes_for :user  
end
and form view code:
<%= form_for(@team) do |f| %>
  <% if @team.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@team.errors.count, "error") %> prohibited this team from being saved:</h2>
      <ul>
      <% @team.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <%= f.fields_for :memberships do |m| %>
    <div class="field">
      <%= m.label :memberships_name %><br>
      <%= m.text_field :name %>
    </div>
    <%= m.fields_for :user do |u| %>
      <div class="field">
        <%= u.label :user_name %><br>
        <%= u.text_field :name %>
      </div>    
    <% end %>
  <% end %>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
Also, please make sure you change this in your controller:
# GET /teams/new
def new
  @team = Team.new
  3.times do # number of members you need to generate!
    @team.memberships.build{ |m| m.build_user } 
  end
end
# GET /teams/1/edit
def edit
end
# POST /teams
# POST /teams.json
def create
  @team = Team.new(team_params)
  respond_to do |format|
    if @team.save
      format.html { redirect_to @team, notice: 'Team was successfully created.' }
      format.json { render action: 'show', status: :created, location: @team }
    else
      format.html { render action: 'new' }
      format.json { render json: @team.errors, status: :unprocessable_entity }
    end
  end
end
private
  # Use callbacks to share common setup or constraints between actions.
  def set_team
    @team = Team.find(params[:id])
  end
  # Never trust parameters from the scary internet, only allow the white list through.
  def team_params
    params.require(:team).permit(:name, memberships_attributes: [:id, :name, user_attributes: [:id, :name]])
  end
Although you can do this in Rails console to do a quick code validation:
team_params = {"name"=>"Team", "memberships_attributes"=>{"0"=>{"name"=>"Membership 1", "user_attributes"=>{"name"=>"User 1"}}, "1"=>{"name"=>"Membership 2", "user_attributes"=>{"name"=>"User 2"}}, "2"=>{"name"=>"Membership 3", "user_attributes"=>{"name"=>"User 3"}}}}
team = Team.new(team_params)
team.save
team.users
#=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, name: "User 1", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">, #<User id: 2, name: "User 2", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">, #<User id: 3, name: "User 3", email: nil, created_at: "2014-09-04 11:25:48", updated_at: "2014-09-04 11:25:48">]>
I hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With