Cocoon add association, how to limit number of associations
I would use javascript for this. You can bind to an event that is triggered upon insert of a new item: on this event count how many items there are, and hide the link if needed.
Likewise, when loading the page do the same.
So in code that would look like:
$(function() {
function check_to_hide_or_show_add_link() {
if ($('#colours .nested-fields:visible').length == 5) {
$('#colours .links a').hide();
} else {
$('#colours .links a').show();
}
}
$('#colours').on('cocoon:after-insert', function() {
check_to_hide_or_show_add_link();
});
$('#colours').on('cocoon:after-remove', function() {
check_to_hide_or_show_add_link();
});
check_to_hide_or_show_add_link();
});
Something like this should work. Note this code is not tested :)
Hope this helps.
Cocoon gem: How to limit the number of associations
Getting rid of ()
on .length
ended up being the solution for the error message. Just a problem with my conversion of javascript to coffeescript.
Here's a link to the new question I opened up to further delve into my specific problem, in case anyone has the same: Javascript: Using a method from model in javascript code
Add limit to coccon gem
<script type="text/javascript">
$(function() {
// console.log('dfdsfdsf')
// limits the number of categories
$('#secondaryparents').on('cocoon:after-insert', function() {
$('#add-category a').hide();
});
$('#secondaryparents').on('cocoon:after-remove', function() {
$('#add-category a').show();
});
if (<%= @parent.secondaryparents.length %>) {
$('#add-category a').hide();
}
})
</script>
Cocoon how to remove associations
When using strong parameters, you have to make sure to permit :id
and :_destroy
, as documented by the way (see documentation ).
It is not unlogical: cocoon sets the _destroy
if something needs to be removed, rails then needs the id to know what to remove.
Rails Nested Fields With Cocoon - link_to_add_association is Deleting Records
As documented this is weird but well known behaviour when using a has_one
relationship. By default cocoon uses the association to create the new nested item.
But then rails assumes, if you already have one, creating a new one will replace it (which does make sense).
However: when using the link_to_add_association
we pre-create an empty item to fill, so it will always delete it.
There is a simple workaround: for has_one
associations you can use the force_non_association_create: true
which will not create the child element using the association, and thus will not remove existing items (for has_one
associations).
So in your case you would write:
<%= link_to_add_association '+ rotap analysis', f, :rotap_analysis,
partial: 'materials/rotap_analysis_fields',
force_non_association_create: true,
class: "btn btn-info btn-xs" %>
Join table Quantity with cocoon (has_many through)
Following this tutorial and looking at the cocoon's demo app. It was easy to solve my problem, although I actually don't know exactly what I did here the <%= link_to_add_association 'adicionar ingrediente', f, :meal_ingredients, 'data-association-insertion-node' => "#ingredients ol", 'data-association-insertion-method' => "append", :wrap_object => Proc.new {|quantity| quantity.build_ingredient; quantity }, :class => "btn btn-default" %>
I hate coding this way but this time I just went with the flow very carefully...
If someone has already used cocoon for that purpose and can explain better I - and the community - would appreciate :D
Changes made:
Model
class MealIngredient < ActiveRecord::Base
belongs_to :meal
belongs_to :ingredient
accepts_nested_attributes_for :ingredient, :reject_if => :all_blank
end
Controller
class MealsController < ApplicationController
before_action :set_meal, only: [:edit, :update, :show]
autocomplete :ingredients, :name
def index
@meals = Meal.order(updated_at: :desc).paginate(:page => params[:page], :per_page => 4)
end
def show
end
def new
@meal = Meal.new
end
def create
@meal = Meal.new(meal_params)
if @meal.save
flash[:success] = "Refeição criada com sucesso!"
redirect_to meals_path
else
render :new
end
end
def edit
end
def update
if @meal.update(meal_params)
flash[:success] = "Your meal was updated succesfully!"
redirect_to meal_path(@meal)
else
render :edit
end
end
private
def meal_params
params.require(:meal).permit(:name, :picture, :tcarb, :tprot, :tfat, :tkcal, meal_ingredients_attributes: [:quantity, ingredient_attributes: [:id, :name, :unit, :carb, :prot, :fat, :_destroy]])
end
def set_meal
@meal = Meal.find(params[:id])
end
end
Views
_form.html.erb
<div class = "row">
<div class= "col-md-10 col-md-offset-1">
<%= simple_form_for(@meal) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name %>
</div>
<h3>ingredientes</h3>
<fieldset id="ingredients">
<ol>
<%= f.fields_for :meal_ingredients do |meal_ingredient| %>
<%= render "meal_ingredient_fields", :f => meal_ingredient %>
<% end %>
</ol>
<div class="form-actions">
<div><%= link_to_add_association 'adicionar ingrediente', f, :meal_ingredients, 'data-association-insertion-node' => "#ingredients ol", 'data-association-insertion-method' => "append", :wrap_object => Proc.new {|quantity| quantity.build_ingredient; quantity }, :class => "btn btn-default" %></div>
</div>
</fieldset>
<div class="form-actions">
<div><%= f.button :submit, class: "btn btn-success" %></div>
</div>
<% end %>
</div>
</div>
_meal_ingredient_fields.html.erb
<div class = "nested-fields">
<table class= "table">
<thead>
<tr>
<td>
<%= f.label "Nome" %>
</td>
<td>
<%= f.label "Unidade" %>
</td>
<td>
<%= f.label "Carbo" %>
</td>
<td>
<%= f.label "Prot" %>
</td>
<td>
<%= f.label "Gordura" %>
</td>
<td>
<%= f.label "Quantidade" %>
</td>
<td>
<%= f.label "kcal" %>
</td>
</tr>
</thead>
<tbody>
<tr>
<%= f.fields_for :ingredient do |fi| %>
<td scope="row" class="col-md-4">
<%= fi.text_field :name, required: true, class: "form-control" %>
</td>
<td class="col-md-2">
<%= fi.text_field :unit, required: true, class: "form-control" %>
</td>
<td class="col-md-1">
<%= fi.text_field :carb, required: true, :pattern => '^\d+(\.\d+)*$', title: "Apenas números separados por pontos", class: "form-control" %>
</td>
<td class="col-md-1">
<%= fi.text_field :prot, required: true, :pattern => '^\d+(\.\d+)*$', title: "Apenas números separados por pontos", class: "form-control" %>
</td>
<td class="col-md-1">
<%= fi.text_field :fat, required: true, :pattern => '^\d+(\.\d+)*$', title: "Apenas números separados por pontos", class: "form-control" %>
</td>
<% end %>
<td class="col-md-1">
<%= f.number_field :quantity, required: true, :pattern => '^\d+(\.\d+)*$', title: "Apenas números separados por pontos", class: "form-control" %>
</td>
<td class="col-md-1">
</td>
<td class="col-md-1">
<%= link_to_remove_association "remove item", f, :class => "btn btn-danger" %>
</td>
</tr>
</tbody>
</table>
</div>
Update multiple checkboxes on association model through nested attributes with Cocoon gem in Rails
Finally got an answer for this task. My whole approach was wrong, trying to insert directly into a join table (That's never going to work!).
This article helped me figure out my mistake, after reading it multiple times. Ended up fixing the problem in 3 mins, after taking about 3 days to search for a solution.
In your model, accept nested attributes for the join table like so:
accepts_nested_attributes_for :tasks_test_methods, reject_if: :all_blank, allow_destroy: true
While reading the cocoon gem documentation, I found this statement that makes sense. When saving nested items, theoretically the parent is not yet saved on validation, so rails needs help to know the link between relations. There are two ways: either declare the belongs_to as optional: false, but the cleanest way is to specify the inverse_of: on the has_many. That is why we write : has_many :tasks, inverse_of: :test_method
Now in my Task
model, I have
has_many :tasks_test_methods, inverse_of: :task, :dependent => :destroy
has_many :test_methods, :through => :tasks_test_methods
Also in my TestMethod
model, I have
has_many :tasks_test_methods, inverse_of: :test_method, :dependent => :destroy
has_many :tasks, :through => :tasks_test_methods
And then in the TasksController
I added this to the params, test_method_ids: []
And finally in the form I have this:
.form-group
= f.collection_check_boxes :test_method_ids, @test_methods, :id, :name do |test_method|
.form-check.mb-4
= test_method.check_box(class: "form-check-input")
%label.form-check-label
%span.ml-2
= test_method.label
And when now your HTML element should look like this:
<div class="form-check mb-4">
<input class="form-check-input" type="checkbox" value="1" name="task[test_method_ids][]" id="task_test_method_ids_1">
<label class="form-check-label">
<span class="ml-2">
<label for="task_test_method_ids_1">Visual Testing (VT)</label>
</span>
</label>
</div>
Hope this helps.
Rails3 Cocoon Validate Nested Field Count
Hm. You can try something like this
def check_link_count
if self.links.reject(&:marked_for_destruction?).count > 5
self.errors.add :base, "No more than 5 links allowed."
end
end
Related Topics
Prefer %W(...) to a Literal Array
Installing Rmagick Gem -- Can't Find Magickwand.H
How to Check Ruby Syntax Error in Ruby Code
Rails 3.1 - Changing Default Scaffold Views and Template
Can't Reindex Sunspot Solr - Error - Rsolr::Error::Http - 500 Internal Server Error
Rails 3 Cli Executes Commands Really Slow
Capybara Synchronize with Has_No_Css
Error Loading Active Record Gem with Sinatra App Using Rvm
How to Give a Sub-Module the Same Name as a Top-Level Class
Ruby Variable Name with Double Underscores
How Do Version Numbers Work for Mri Ruby
Why Aren't Global (Dollar-Sign $) Variables Used
Sass Variables Not Parsing Correctly - Undefined Variable: "$Ct-White"
Wrapping Text into Lines at Word Boundaries
How to More Elegantly Remove Duplicate Items Across All Elements of a Ruby Array