Cocoon Add Association, How to Limit Number of Associations

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



Leave a reply



Submit