0

I'm trying to save checkbox selections in a has_and_belongs_to_many relationship. I want to link each street to the maps that it is found on. From the map table a list of checkboxes is shown on new or edit views. But I'm missing something in linking this all together. It seems like the list of maps should be saved with each street item, but I'm not figuring out how to do that. I have a map field references for that purpose.

Models without validations:

class Street < ApplicationRecord
  has_and_belongs_to_many :maps
end

class Map < ApplicationRecord
  has_and_belongs_to_many :streets
end

streets_controller.rb (relevant parts):

class StreetsController < ApplicationController
  before_action :set_street, only: [:show, :edit, :update, :destroy]

  def new
    @street = Street.new
    gon.streetExtentArray = @street.extent_array
    @maps = Map.all.order(:year)
  end

  def edit
    @maps = Map.all.order(:year)
    gon.streetExtentArray = @street.extent_array
    gon.streetExtentJson = @street.extent_json
  end
  def create
    @street = Street.new(street_params)

    respond_to do |format|
      if @street.save
        format.html { redirect_to @street, notice: "Street was successfully created.  #{undo_link}" }
        format.json { render :show, status: :created, location: @street }
      else
        format.html { render :new }
        format.json { render json: @street.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    respond_to do |format|
      if @street.update(street_params)
        format.html { redirect_to @street, notice: "Street was successfully updated.  #{undo_link}" }
        format.json { render :show, status: :ok, location: @street }
      else
        format.html { render :edit }
        format.json { render json: @street.errors, status: :unprocessable_entity }
      end
    end
  end

  private  
    def street_params
      params.require(:street).permit(:city, :previous_name, :current_name, :date_earliest, :date_latest, :cross_streets, :extent_json, :extent, :extent_length, :extent_array, :number_of_blocks, :references, :ref1, :ref2, :ref3, :notes, {:reference_ids => []})
    end
end

Relevant part of the new and edit view template:

<%= form_with(model: @street, local: true) do |form| %> 

<%= form.label "City" %>
  <%= form.text_field :city, id: :street_city %>
</div>

< and many other  fields >

<%= form.collection_check_boxes( :references, @maps, :id, :name, bootstrap: {check_inline: true}, class: "") do |b|
b.label(:"data-value" => b.value) { b.check_box + b.text }
end %>

<%= form.submit%>

The generated HTML:

<input type="hidden" name="street[map_ids][]" value="" />
<input type="checkbox" value="12" name="street[map_ids][]" id="street_map_ids_12" /><label for="street_map_ids_12">1857 Bancroft</label>
<input type="checkbox" value="7" name="street[map_ids][]" id="street_map_ids_7" /><label for="street_map_ids_7">1888 Sanborn</label>
and six more similar checkboxes

The migration for the join table:

class CreateJoinTableMapsStreets < ActiveRecord::Migration[5.1]
  def change
    create_join_table :maps, :streets do |t|
      t.index [:map_id, :street_id]
      t.index [:street_id, :map_id]
    end
  end
end

No errors, but nothing is saved. And I don't really understand where and how this should be saved. I've created a field named references in the Streets table, but there is no reference to this. And it doesn't seemed like it should be saved in the maps_streets table and it's not.

I considered having the more robust has_many and belongs_to relationship, but I don't think I'm going to need it and can't see how it would help.

Params? "street"=>{"city"=>"Los Angeles", "previous_name"=>"3rd St", "current_name"=>"Miramar St. One block abandoned", "date_earliest"=>"1921", "date_latest"=>"", "cross_streets"=>"Miramar. And gone one block between Lucas and Bixel", "number_of_blocks"=>"2", "extent_length"=>"", "references"=>["", "7"], "ref1"=>"Baist 1921", "ref2"=>"", "ref3"=>"", "notes"=>"3rd was jogged south starting at Beaudry to what was Crown Hill at Huntley/Boylston", "extent_json"=>"{\"type\":\"LineString\",\"coordinates\":[[-118.26117539280003,34.05901974362122],[-118.2593849946753,34.05823410691563],[-118.25815599257271,34.05768101430694],[-118.25759459655055,34.05717191451128],[-118.25663111959356,34.05654339202722]]}", "extent_array"=>"[[34.05900599436149,-118.26117038726808],[34.057672720136964,-118.25815558433534],[34.057174959049995,-118.25757622718811],[34.0565527535807,-118.25666427612306]]"}, "commit"=>"Update Street", "controller"=>"streets", "action"=>"update", "id"=>"645"} One checkbox checked

Greg
  • 2,359
  • 5
  • 22
  • 35

1 Answers1

1

I think you need just a few changes.

In your Controller, add map_ids to the permitted params.

private  
  def street_params
    params.require(:street).permit(:city, :previous_name, :current_name,
     :date_earliest, :date_latest, :cross_streets, :extent_json, :extent,
     :extent_length, :extent_array, :number_of_blocks, :notes, :map_ids => [])
  end

In your form, the collection_check_boxes should be for :map_ids

<%= form_with(model: @street, local: true) do |form| %> 

  <%= form.collection_check_boxes( :map_ids, @maps, :id, :name, bootstrap: {check_inline: true}, class: "") do |b|
      b.label(:"data-value" => b.value) { b.check_box + b.text }
  <% end %>

<% end %>
Pablo
  • 3,004
  • 1
  • 12
  • 19
  • Good, and it is being stored in `maps_streets` table. Now I'll try and figure out what's going on so can display the info in `show`. Thank you @Pablo – Greg Feb 22 '18 at 00:50
  • sorry. :map_ids – Pablo Feb 22 '18 at 00:51
  • Is there a short explanation? I get that the `has_and_belongs_to_many` is the link, but I can't quite see the how `map_ids` is the correct identifier. Thank you @Pablo – Greg Feb 22 '18 at 01:03
  • As it is a HABTM relation, each street has_many :map_streets (the join table). And each street has_many :maps through: :map_streets. So it accepts map_ids (an array of map_id) – Pablo Feb 22 '18 at 01:17
  • I hope that sinks in. – Greg Feb 22 '18 at 01:18