Search code examples
ruby-on-railsmany-to-manycocoon-gem

Cocoon Gem: Edit view is only showing empty form fields


I am using cocoon in my Rails app for assigning employees (users) to projects (many to many connection). The creation of associations is working correctly, but each time I add another employee cocoon adds an empty form field in the edit view. None of the other cocoon form fields in the edit view are populated either. Could this be due to the usage of dropdowns (select)?

When I inspect the form in my browser I can see that each field seems to be assigned to one of the associations, but the selection is still empty. enter image description here

What I would like to achieve is, that every association is displayed in a cocoon form field, so that they can be edited. Thanks for any help in advance!

My code is below (Sorry for any mess, it is my first time trying out a many to many connection of two models).

Project Edit View

 <%= form_for(@project, :url => project_path, method: :patch) do |f| %>

    <div class="form-group">
        <%= f.label :title %>
        <%= f.text_field :title, class: "form-control" %>
    </div>

    <div class="form-group">
        <%= f.label :customer %>
        <%= f.text_field :customer, class: "form-control" %>
    </div>

    <%= f.fields_for :user_projects do |collab| %>
        <% collab.hidden_field :project_id, value: @project.id %>
        <%= render 'user_project_fields', f: collab %>
    <% end %>
    <div class="add-collaborator">
        <%= link_to_add_association "add", f, :user_projects, class: "btn btn-mmc" %>
    </div>

    <div class="actions">
        <%= f.submit "Save Changes", class: "btn btn-mmc btn-mmc-medium" %>
    </div>
<% end %>

cocoon field partial

<div class="nested-fields">
    <%= f.label "Select User" %>
    <div class="form-group custom-form-group">
        <%= f.select(:user_id, options_for_select(User.all.map { |u| [u.email, u.id] }), {include_blank: true}, {class: 'form-control'})%>
        <div class="btn-user-project">
            <%= link_to_remove_association "x", f, class: "btn btn-mmc-attention btn-mmc" %>
        </div>
    </div>
</div>

Project Model

class Project < ApplicationRecord
    has_many :user_projects
    has_many :users, :through => :user_projects

    accepts_nested_attributes_for :user_projects, reject_if: :all_blank, allow_destroy: true
end

User Model

class User < ApplicationRecord
    # Include default devise modules. Others available are:
    # :confirmable, :lockable, :timeoutable and :omniauthable
    devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable

    has_many :user_projects
    has_many :projects, :through => :user_projects
end

Project Controller

def edit
    @project = Project.find(params[:id])
    @project.user_projects.build
end

def update
    @project = Project.find(params[:id])
    @project.update(project_params)
    @new_collaborator = UserProject.new(user_id: params[:user_id], project_id: params[:project_id])
    @new_collaborator.save
    if @project.update(project_params) && @new_collaborator.save
        redirect_to projects_path
    else
        render :edit
    end
end

private

def project_params
    params.require(:project).permit(:title, :customer, :delted, user_projects_attributes: [:user_id, :project_id]).reject { |_, v| v.blank? }
end

Solution

  • I am guessing the mapping to the actual value is not done correctly, e.g. the value of the user_id is not marked as selected, in the options_for_select you have to add the selected value as parameter (see documentation).

    However, there is a much easier version:

    <%= f.collection_select(:user_id, User.all, :id, :email) %>
    

    BTW using a gem like simple_form also makes building forms a lot more intuitive and straightforward.