Search code examples
ruby-on-railsruby-on-rails-5has-and-belongs-to-many

Rails 5 has_and_belongs_to_many form


I have a little app that has a clients and a sites model. I want to create a new site in a modal from the show page but i get and error.

Please find below schema, controller, models and error.

schema

create_table "clients", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "clients_sites", id: false, force: :cascade do |t|
    t.bigint "client_id", null: false
    t.bigint "site_id", null: false
  end

create_table "sites", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

I want to create a site from my client#show page

<div class="page-header">
  <%= link_to clients_path, class: 'btn btn-default' do %>
    <span class="glyphicon glyphicon-list-alt"></span>
    All Clients
  <% end %>
  <%= link_to edit_client_path(@client), class: 'btn btn-primary' do %>
    <span class="glyphicon glyphicon-pencil"></span>
    Edit
  <% end %>
  <h1>Show client</h1>
</div>

<dl class="dl-horizontal">
  <dt>Name:</dt>
  <dd><%= @client.name %></dd>

</dl>


<div class="row">
  <div class="col-sm-6">
    <h1>Sites</h1>
  </div>

  <div class="col-sm-6 text-right">
    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
      Add New Site
    </button>

    <!-- Modal -->
    <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
              <span aria-hidden="true">&times;</span>
            </button>
          </div>
          <div class="modal-body">
            <%= form_for [@client, @site] do |f| %>
              <%= form.label :name %>
              <%= form.text_field :name, class: 'form-control' %>
              <%= form.submit class: 'btn btn-primary' %>
            <% end %>
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary">Save changes</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<div class="table-responsive">
  <table class="table table-striped table-bordered table-hover">
    <thead>
    <tr>

      <th>Name</th>



    </tr>
    </thead>

    <tbody>
    <% @client.sites.each do |site| %>
      <%= content_tag :tr, id: dom_id(site), class: dom_class(site) do %>

        <td><%= link_to site.name, site %></td>




      <% end %>
    <% end %>
    </tbody>
  </table>
</div>

clients controller

  def show
    @client = Client.find(params[:id])
    @site = Site.new
  end

app/models/client.rb

class Client < ApplicationRecord
  has_and_belongs_to_many :sites
end

app/models/site.rb

class Site < ApplicationRecord
  has_and_belongs_to_many :clients
end

when i hit the page i get the following error

ActionView::Template::Error (undefined method `client_sites_path' for #<#<Class:0x00007f9329282490>:0x00007f9329279890>
Did you mean?  clients_path
               edit_site_path):
    39:             </button>
    40:           </div>
    41:           <div class="modal-body">
    42:             <%= form_for [@client, @site] do |f| %>
    43:               <%= form.label :name %>
    44:               <%= form.text_field :name, class: 'form-control' %>
    45:               <%= form.submit class: 'btn btn-primary' %>

app/views/clients/show.html.erb:42:in `_app_views_clients_show_html_erb___1421137287308647677_70135013712680'

edit

routes

 sites GET    /sites(.:format)                                                                         sites#index
                           POST   /sites(.:format)                                                                         sites#create
                  new_site GET    /sites/new(.:format)                                                                     sites#new
                 edit_site GET    /sites/:id/edit(.:format)                                                                sites#edit
                      site GET    /sites/:id(.:format)                                                                     sites#show
                           PATCH  /sites/:id(.:format)                                                                     sites#update
                           PUT    /sites/:id(.:format)                                                                     sites#update
                           DELETE /sites/:id(.:format)                                                                     sites#destroy
                   clients GET    /clients(.:format)                                                                       clients#index
                           POST   /clients(.:format)                                                                       clients#create
                new_client GET    /clients/new(.:format)                                                                   clients#new
               edit_client GET    /clients/:id/edit(.:format)                                                              clients#edit
                    client GET    /clients/:id(.:format)                                                                   clients#show
                           PATCH  /clients/:id(.:format)                                                                   clients#update
                           PUT    /clients/:id(.:format)                                                                   clients#update
                           DELETE /clients/:id(.:format)                                                                   clients#destroy

Rails.application.routes.draw do
  resources :sites
  resources :clients

Solution

  • ActionView::Template::Error (undefined method `client_sites_path' for Class:0x00007f9329282490>:0x00007f9329279890 Did you mean? clients_path edit_site_path):

    The error says there is no such available path helper called client_sites_path. Indeed its true as you don't have your routes defined such way. As per your comment on @arieljuod post, I understood that you want to save sites to clients. The below code will help you to achieve what you want

    <%= form_for @site do |f| %>
      <%= form.label :name %>
      <%= form.text_field :name, class: 'form-control' %>
      <%= form.select :client_ids, options_from_collection_for_select(Client.all, :id, :name), :prompt => "Select Clients", :multiple => true %>
      <%= form.submit class: 'btn btn-primary' %>
    <% end %>
    

    This piece of code snippet

    <%= form.select :client_ids, options_from_collection_for_select(Client.all, :id, :name), :prompt => "Select Clients", :multiple => true %>
    

    creates a dropdown where you can select one or more clients to add to the sites which will get submitted along with the params that goes to the sites#create action.

    Also you should make sure to whitelist client_ids: [] by adding it to the site_params method. By doing this, Rails under the hood uses this client_ids values to generate entries for clients_sites table. Thus completing the creation of sites to the clients.