Trying to use to Cocoon gem to create nested forms for a recipe app, but doesn't allow me to save new recipes although I've added the ingredient name and recipe steps.
I keep getting the following errors in my localhost:
2 Prevented this recipe from saving
- Ingredients recipe must exist
- Directions recipe must exist
I followed a tutorial, have read the Cocoon documentation and research about this issue on Google and SO, but still can't find it. I've been trying to solve this for a few days. Would really appreciate some help please.
Model
class Recipe < ActiveRecord::Base
belongs_to :user
has_many :ingredients
has_many :directions
accepts_nested_attributes_for :ingredients,
reject_if: proc { |attributes| attributes['name'].blank? },
allow_destroy: true
accepts_nested_attributes_for :directions,
reject_if: proc { |attributes| attributes['step'].blank? },
allow_destroy: true
validates :title, :description, :image, presence: true
has_attached_file :image, styles: { :medium => "400x400#" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
views/recipes/_form
<%= simple_form_for @recipe do |f| %>
<% if @recipe.errors.any? %>
<div id="error">
<p><%= @recipe.errors.count %> Prevented this recipe from saving</p>
<ul>
<% @recipe.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="panel-body">
<%= f.input :title %>
<%= f.input :description %>
<%= f.input :image %>
</div>
<div class="row">
<div class="col-md-6">
<h3>Ingredients</h3>
<div id="ingredients">
<%= f.simple_fields_for :ingredients do |ingredient| %>
<%= render 'ingredient_fields', f: ingredient %>
<% end %>
<div class="links">
<%= link_to_add_association 'Add Ingredient', f, :ingredients, partial: 'ingredient_fields', class: "btn btn-default add-button" %>
</div>
</div>
</div>
<div class="col-md-6">
<h3>Directions</h3>
<div id="directions">
<%= f.simple_fields_for :directions do |direction| %>
<%= render 'direction_fields', f: direction %>
<% end %>
<div class="links">
<%= link_to_add_association 'Add Step', f, :directions, partial: 'direction_fields', class: "btn btn-default add-button" %>
</div>
</div>
</div>
</div>
<div>
<%= f.button :submit, class: "btn btn-primary" %>
</div>
views/recipes/_ingredient_fields
<div class="form-inline clearfix">
<div class="nested-fields">
<%= f.input :name, class: "form-control" %>
<%= link_to_remove_association 'Remove', f, class: "btn btn-default" %>
</div>
</div>
views/recipes/_direction_fields
<div class="form-inline clearfix">
<div class="nested-fields">
<%= f.input :step, class: "form-control" %>
<%= link_to_remove_association 'Remove Step', f, class: "btn btn-default" %>
</div>
</div>
controller
class RecipesController < ApplicationController
before_action :find_recipe, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
@recipe = Recipe.all.order("created_at DESC")
end
def show
end
def new
@recipe = current_user.recipes.build
end
def create
@recipe = current_user.recipes.build(recipe_params)
if @recipe.save
redirect_to @recipe, notice: "Successfully created new recipe"
else
render 'new'
end
end
def edit
end
def update
if @recipe.update(recipe_params)
redirect_to @recipe
else
render 'edit'
end
end
def destroy
@recipe.destroy
redirect_to root_path, notice: "Successfully deleted recipe"
end
private
def recipe_params
params.require(:recipe).permit(:title, :description, :image, ingredients_attributes: [:id, :name, :_destroy], directions_attributes: [:id, :step, :_destroy])
end
def find_recipe
@recipe = Recipe.find(params[:id])
end
end
Here is a link to the repository if it helps debugging: https://github.com/rchrdchn/rails-projects/tree/master/recipe_box
When you're creating a new recipe, you don't have the recipe object, because it's in the server memory. But when you're updating it, the recipe object is persisted.
That's why you're getting the Ingredients recipe must exist and directions recipe must exist error.
To fix that you have to add the inverse_of in the associations.
class Recipe
has_many :ingredients, inverse_of: :recipe
has_many :directions, inverse_of: :recipe
class Ingredient
belongs_to :recipe, inverse_of: :ingredients
class Direction
belongs_to :recipe, inverse_of: :directions