3
votes

I have a has_many through join table setup for a recipe app where Ingredient and Meal connect through MealIngredient. Within MealIngredient, I have meal_id, ingredient_id, and amount.

My question is: How can I save and update the amount column in the meal form?

My form field for adding an ingredient looks like this:

<% Ingredient.all.each do |ingredient| %>
  <label>
    <%= check_box_tag "meal[ingredient_ids][]", ingredient.id, f.object.ingredients.include?(ingredient) %>
    <%= ingredient.name %>
  </label>
  <br />
<% end %>

How do I save the amount for each ingredient?

I am referencing this question found here: Rails 4 Accessing Join Table Attributes

1

1 Answers

3
votes

I made a demo for you: http://meals-test2.herokuapp.com/new enter image description here

--

If you're using a form, you need to use fields_for and edit it that way:

#app/controllers/meals_controller.rb
class MealsController < ApplicationController
  def edit
    @meal = Meal.find params[:id]
  end

  private

  def meal_params
    params.require(:meal).permit(meal_ingredient_attributes: [:amount])
  end
end

#app/views/meals/edit.html.erb
<%= form_for @meal do |f| %>
  <%= fields_for :meal_ingredients do |i| %>
      <%= f.object.ingredient.name #-> meal_ingredient belongs_to ingredient %>
      <%= i.number_field :amount %>
  <% end %>
  <%= f.submit %>
<% end %>

The above will output a list of ingredients for the meal and allow you to input the "amount" value.

As for checkboxes, I'd have to make a demo app to see if I can get that working. I can do this if you feel it necessary.


Another way is with has_and_belongs_to_many:

#app/models/meal.rb
class Meal < ActiveRecord::Base
  has_and_belongs_to_many :ingredients do
     def amount #-> @meal.ingredients.first.amount
        ...........
     end
  end
end

#app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
  has_and_belongs_to_many :meals
end

This way, you'll be able to add as many meals / ingredients as required, allowing you to find the "amount" with @meal.ingredients.where(ingredients: {id: "x" }).size. You could also make a method to simplify it (above).

You wouldn't need to use fields_for for this:

#app/controllers/meals_controller.rb
class MealsController < ApplicationController
  def new
     @meal = Meal.new
  end
  def edit
     @meal = Meal.find params[:id]
  end

  def update
     @meal = Meal.find params[:id]
     @meal.save
  end

  def create
     @meal = Meal.new meal_params
     @meal.save
  end

  private

  def meal_params
    params.require(:meal).permit(ingredient_ids: [])
  end
end

Because the HABTM record uses the has_many association in your model, it provides you with the collection_singular_ids method. This allows you to override the associated data without fields_for:

#app/views/meals/new.html.erb
<%= form_for @meal do |f| %>
  <%= f.collection_check_boxes :ingredient_ids, Ingredient.all, :id, :name %>
  <%= f.submit %>
<% end %>

If you wanted to add extra ingredients, you'd need to create JS to duplicate the checkbox element. This will then allow you to submit multiple ids to the controller, which will just insert them blindly into the db.

This method overrides the ingredients list, and only works if you don't have any uniqueness constraints on the habtm association / table.