jimgug
jimgug

Reputation: 175

Rails Multiple checkboxes with associated Text fields

I'm trying to create a Product form that has multiple sizes and prices for each of those sizes.

They way I have it modelled is a has_many :through relationship. The associative table contains an extra field for price such that it will now hold the product_id, size_id, and price.

I'm not sure how I should go about creating my form or how Rails expects this to look. Any help would be much appreciated.

My Product is Cake :)

class Cake < ApplicationRecord
  belongs_to :cake_type

  has_many :cake_details
  has_many :sizes, through: :cake_details
end

Size model

class Size < ApplicationRecord
  has_many :cake_details
  has_many :cakes, through: :cake_details
end

CakeDetail model class CakeDetail < ApplicationRecord belongs_to :cake belongs_to :size end

my migration

class CreateCakeDetails < ActiveRecord::Migration[5.1]
  def change
    create_table :cake_details do |t|
      t.references :cake, foreign_key: true
      t.references :size, foreign_key: true
      t.decimal :price, :precision => 10, :scale => 2
      t.timestamps
    end
  end
end

The only thing I'm stuck on is associating the form with the model. i.e. for every size I want to have a text box with price associated with it.

This is currently how I'm approaching it but I have no idea how rails expects the id's of the text box to look or how I should structure this.

This is currently what I'm experimenting with in my form

    <%= collection_check_boxes(:cake, :size_ids, Size.all, :id, :name) do |b| %>
    <tr>
      <td>
        <%= b.label %>
      </td>
      <td>
        <%= b.check_box %>
      </td>
      <td>
        <%= form.text_field :cake_detail, id: b.label %>
      </td>
    </tr>
<% end %>

Upvotes: 0

Views: 543

Answers (1)

yeuem1vannam
yeuem1vannam

Reputation: 957

The way you define your business logic is normal

- A product has multiple sizes
- Each size has a price

The only thing I believe that it leads you to the problem is you are trying to create everything at the same time. Even Rails has nested_attributes which might solve your problem, but let's think once again.
Generally, Size records are fixed and was created beforehand. So that you don't have to create it at the same time with creating a Product.
Once you deal with this idea, your problem becomes much easier:

  • You had a list of Size: M, L, XL, XXL ... that was created beforehand
    ( You may create them via db/seeds.rb )
  • You want to create Product along ProductDetail with prices,
    and link the ProductDetail with Size

Now you can use Rails's nested_attributes for the relation Product -> ProductDetail

Your model

# app/models/cake.rb
class Cake < ApplicationRecord
  belongs_to :cake_type

  has_many :cake_details
  has_many :sizes, through: :cake_details

  attr_accessor :is_enable
  accepts_nested_attributes_for :cake_details, reject_if: :is_not_good_detail?

  private
  def is_not_good_detail?(attributed)
    return true if attributed[:is_enable].to_i != 1
    # Check if CakeDetail is good or not
  end
end

Your controller

# app/controllers/cakes_controller.rb
class CakesController < ApplicationController
  def new
    @cake = Cake.new
    # Build `cake_details`, so that you can render them at view
    Size.each do |size|
      @cake.cake_details.build(size_id: size.id, price: 0)
    end
  end

  def create
    # Create your Cake + CakeDetail
  end

  private

  def cake_params
    # permit your params here
  end
end

Your view

# app/views/cakes/_form.html.erb
<%= form_for @cake do |f| %>
  <%= f.fields_for :cakes_detail do |field| %>
    <%= field.check_box :is_enable %>
    <%= field.hidden_field :size_id %>
    <%= field.text_field :price %>
  <% end>
<% end %>

My code is completely not tested, and you still have a lot of things to do, but it should be the right way to solve your problem, tho.
You can consider the checklist to make it done:

  • Display name of size. Ex: XL, XXL
  • Permit the right params
  • Reject the invalid CakeDetail attribute set.
  • Avoid duplicate of size for a product when updating

<< Update >>>

Since the check_box only produces 0 and 1 value, so using it for size_id is incorrect. We can solve it by:

  • add an attr_accessor (ex: is_enable) for CakeDetail and use it for the check_box
  • size_id become a hidden field
  • Reject attributes if is_enable != 1

You can found here a working example yeuem1vannam/product-size

Upvotes: 0

Related Questions