Rails - Cocoon gem - Nested Forms - ruby-on-rails

I am trying to make an app with Rails 4.
I use simple form gem for forms and am trying to use Cocoon gem for nested elements of the forms.
I asked this question, which explains what I'm trying to achieve in more detail: Rails - Multiple entries for a single attribute
I have a profile model and a qualifications model. The associations are:
profile.rb
has_many :qualifications
accepts_nested_attributes_for :qualifications, reject_if: :all_blank, allow_destroy: true
qualification.rb
belongs_to :profile
the profiles controller includes the qualification attributes in the strong params:
def profile_params
params[:profile].permit(:user_id, :title, :hero, :overview, :research_interest, :occupation, :external_profile,
:working_languages, :tag_list,
qualifications_attributes: [:id, :level, :title, :year_earned, :institution, :_destroy] )
end
My profile form now has:
<div class="intpol2">
Your qualifications
</div>
<%= render 'qualifications/qualification_fields', f: f %>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_add_association 'Add a qualification', f: f, partial: 'qualifications/qualification_fields' %>
</div>
My qualifications partial form is now named in my qualification views folder as _qualifications_fields.html.erb
<div class="nested-fields">
<div class="container-fluid">
<%= simple_fields_for :qualifications do |f| %>
<div class="form-inputs">
<div class="row">
<div class="col-md-6">
<%= f.input :title, :label => "Your award" %>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= f.input :level, collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
</div>
<div class="col-md-6">
<%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year) %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<!-- link_to_remove_association 'Remove this qualification', f, :f -->
</div>
</div>
</div>
<% end %>
</div>
</div>
I commented out the remove link, because as is, I get this error:
undefined method `new_record?' for nil:NilClass
I'm baffled by the way Rails reports errors- I rarely understand error messages of this style, but I gather it's something to do with there not being any qualifications to remove. Does cocoon handle this?
When I comment this out and try again, I get this error with the link to add an association:
undefined method `object' for #<Hash:0x007fe70c501ea0>
I don't know what this message means either.
So, by following the steps in the Cocoon gem docs, (with changes because I don't know how to write things in haml; and changing the form of expression to reference the form builder 'f: f' works across the rest of my forms, so Im guessing that the expression shown in the docs might be something to do with haml), I have 2 errors at this stage:
add association - undefined method object
remove association - undefined method `new_record?
I'm struggling to understand what's going on here.
TAKING JEIWAN'S SUGGESTION:
I change my profile form to:
<%= simple_form_for #profile, html: { multipart: true } do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="row">
<div class="intpol2">
Your professional qualifications
</div>
<%= simple_fields_for :qualifications do |f| %>
<%= render 'qualifications/qualification_fields', f: f %>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_add_association 'Add a qualification', f, :qualifications, partial: 'qualifications/qualification_fields' %>
<% end %>
</div>
</div>
<div class="form-actions">
<%= f.button :submit, "Submit", :class => 'formsubmit' %>
</div>
<% end %>
</div>
</div>
</div>
I change my qualification fields form to:
<div class="nested-fields">
<div class="container-fluid">
<div class="form-inputs">
<div class="row">
<div class="col-md-6">
<%= f.input :title, :label => "Your award" %>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= f.input :level, collection: [ "Bachelor's degree", "Master's degree", "Ph.D", "Post Doctoral award"] %>
</div>
<div class="col-md-6">
<%= f.input :year_earned, :label => "When did you graduate?", collection: (Date.today.year - 50)..(Date.today.year) %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= link_to_remove_association 'Remove this qualification', f %>
</div>
</div>
</div>
</div>
</div>
When I save this and try again, I get this error:
undefined method `new_record?' for nil:NilClass
The error message highlights this line:
<%= link_to_remove_association 'Remove this qualification', f %>

link_to_add_association 'Add a qualification', f: f, partial: 'qualifications/qualification_fields'
f: f is incorrect here. The second parameter should be a form builder (just an object, not a hash) and the third parameter – the name of the association, i.e. :qualifications in your case. So it should look like this:
link_to_add_association 'Add a qualification', f, :qualifications, partial: 'qualifications/qualification_fields'
And here:
link_to_remove_association 'Remove this qualification', f, :f
:f is also wrong. Third parameter here is HTML options. If you don't need them, then just specify 2 parameters:
link_to_remove_association 'Remove this qualification', f
Next, _qualification_fields.html.erb shouldn't contain simple_fields_for. This partial is used to render the fields of one distinct association object. It is shown when you click 'Add a qualification' or when your parent object already has some qualifications. simple_fields_for :qualifications should be put in you profile form and contain render 'qualifications/qualifications_fields', f: f and link_to_add_association .... Consider this example: https://github.com/nathanvda/cocoon#simpleform

Related

Adding multiple images on a page with CRUD

I want to add several different images with a crud to make some "work pages" but i struggle with the way to do this. Do I have to update my model or how i can specify that each adding field is for a section of my view? I'm using cloudinary to host the images.
For now when I add an image its all the same but I wanted to add differents ones at each spot.
My work#new view :
<div class="container text center">
<%= simple_form_for(#work) do |f| %>
<div class="row justify-content-center">
<div class="col-md-6 col-sm-12">
<h1 class="heading-02" style="padding: 2rem 0rem;">New Work</h1>
<div class="work-new-section">
<h2 class="sub-heading-03">Header</h2>
<%= f.input :name %>
<%= f.input :description %>
<%= f.input :photo, as: :file %>
</div>
<div class="work-new-section">
<h2 class="sub-heading-03">Tasks</h2>
<%= f.input :task_title %>
<%= f.input :tasks %>
<%= f.input :photo, as: :file %>
</div>
<div class="work-new-section">
<h2 class="sub-heading-03">Content#1</h2>
<%= f.input :content1_title %>
<%= f.input :content1 %>
<%= f.input :photo, as: :file %>
</div>
<div class="work-new-section">
<h2 class="sub-heading-03">Focus</h2>
<%= f.input :focus_title %>
<%= f.input :focus %>
</div>
<div class="work-new-section">
<h2 class="sub-heading-03">Content#2</h2>
<%= f.input :content2_title %>
<%= f.input :content2 %>
<%= f.input :photo, as: :file %>
</div>
<%= f.submit 'Create a new Work', :class => 'button-accent mb-4' %>
</div>
</div>
<% end %>
</div>
Work#new
My Work model :
class Work < ApplicationRecord
has_one_attached :photo
end
For the work#show is use some partials with the same behaviour in it:
<div class="container">
<div class="row">
<div class="col-md-4 col-sm-12">
<%= cl_image_tag #work.photo.key, :class=> "work-image", :width => 420, :height => 236, :crop => "fill" %>
</div>
<div class="col-md-4 col-sm-12">
<h2 class="sub-heading-03"><%= #work.content1_title %></h2>
<p class="ui-regular-01"><%= #work.content1 %></p>
</div>
</div>
</div>
Work#show
How could I specify one particular photo to each section? (task, content#1, content#2 etc...)
Thank you very much for your time!
I tried to change my new view but i have the impression that i omit something that i don't understand very well.

accepts_nested_attributes_fields not showing for join table

I have the following tables
class Region < ActiveRecord::Base
has_many :companies, through: :companies_regions
has_many :companies_regions, :dependent => :destroy
end
class Company < ActiveRecord::Base
has_many :regions, through: :companies_regions
has_many :product_type, dependent: :destroy
has_many :companies_regions, :dependent => :destroy
accepts_nested_attributes_for :companies_regions, :allow_destroy => true
end
class CompaniesRegion < ActiveRecord::Base
belongs_to :company
belongs_to :region
end
I want to create a new company and i want to be able to add a new region accordingly to the CompaniesRegion table.
form.html.erb
<%= simple_form_for(['admin', #company]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<%= f.input :name %>
</div>
<div>
<div class="row">
<div class="col-md-12">
<h4>Basic Coverages</h4>
<div class="row form-group">
<label class="col-md-1">#</label>
<label class="col-md-3">Coverage</label>
<label class="col-md-1">Description</label>
</div>
<div>
<%= f.simple_fields_for :companies_regions do |company_region| %>
<%= render 'company_region', f: company_region %>
<% end %>
<%= link_to_add_association 'New Region', f, :companies_regions, partial: 'company_region' %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
_company_region.html.erb
<div class="nested-fields form-group row">
<div class="col-md-1"></div>
<div class="col-md-3">
<%#= f.select :region_id, class: 'form-control', placeholder: 'Region' %>
<%= f.input_field :region_id, collection: ['Asia', 'America'], class: 'form-control', prompt: 'Please Select' %>
</div>
<div class="col-md-1">
<%= link_to_remove_association(f, title: 'Remove') do %>
<span class="glyphicon glyphicon-remove"></span>
<% end %>
</div>
</div>
The problem here is that when i click the New Region link, i am expecting it to display the details in the _company_region.html.erb but unfortunately it doesnt.
Nothing shows. It doesnt display any data. It however refreshes the page which is absurd.
Dont know if it is because my table is a join table hence the problem or if there is something else that i am missing but based on documentation, this should be fine and should work.
Any help is deeply appreciated
Its a very common misconception that you need to use nested attributes to set assocations. You don't. Rails generates a regions_ids= setter for the association that can be hooked up with checkboxes or a select tag.
All you really need with SimpleForm is:
<%= f.association :regions %>
See the docs for ActionView::Helpers::FormOptionsHelper and associations in SimpleForm for more details on how this works.
<%= simple_form_for(['admin', #company]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<%= f.input :name %>
</div>
<div>
<div class="row">
<div class="col-md-12">
<h4>Basic Coverages</h4>
<div class="row form-group">
<label class="col-md-1">#</label>
<label class="col-md-3">Coverage</label>
<label class="col-md-1">Description</label>
</div>
<div>
<%= f.association :regions %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
The only reason you would need to use nested attributes is if the user must be able to create regions in the same request. But usually its better to use ajax to handle those cases.
Your code looks fine. The behaviour you describe seems to indicate that the cocoon.js code is not included/loaded correctly? Did you
add require 'cocoon' in application.js ?
did you include application.js in your html?

How to add in multiple options for checkbox in Ruby on Rails?

This is likely a super easy answer for a Ruby on Rails expert. I have a form and need to add in a checkbox that has multiple items. I've been messing with the following code for a lot longer than I'd like to admit:
<%= form_for :lead, url: something, html: {id: 'product-form'} do |f|%>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<%= f.label :product%>
<%= f.check_box :product, {multiple:true}, "option", "option2", :class => 'form-control'%>
</div>
</div>
</div>
<% end %>
With this code I get the error "wrong number of arguments (5 for 1..4)".
Basically I just want someone to be able to pick multiple options. I've also tried the following:
<div class="row">
<div class="col-md-12">
<div class="form-group">
<%= f.label :product%>
<%= f.check_box :option1, "Option1" :class => 'form-control'%>
<%= f.check_box :option2, "Option2", :class => 'form-control'%>
</div>
</div>
</div>
And I get the delightful "undefined method `merge' for "Option1":String". What am I missing to put the values in associated with the label?
I use Rails 5.1.6 and this is how I achieved adding multiple options for checkbox. I also placed it in a dropdown.
In the migration file:
t.string :skills
In my controller "tasks_controller.rb":
def task_params
params.require(:task).permit(:work_type, :title, :post, {skills: []})
end
In my model "task.rb" I did:
validates :skills, presence: true
serialize :skills, JSON
I created a module mobilephones_data.rb in directory "app/model/concerns/" which holds all the options of the checkbox as an array that can be edited easily.
In app/model/concerns/mobilephones_data.rb I wrote:
module Mobilenphones_Data
Activities = [
'Amazon Kindle', 'Blackberry', 'iPad', 'iPhone', 'Mobile Phone', 'Nokia',
'Palm', 'Samsung'
]
end
This will put all data from module's array into the checkbox drop-down as checkbox options. In My form I did:
<div class="card">
<a class="card-link card-header" data-toggle="collapse" href="#collapseOne">
Mobile Phones
</a>
<div id="collapseOne" class="collapse" data-parent="#accordion">
<div class="card-body">
<% Mobilenphones_Data::Activities.each do |activity| %>
<div id="skill_list" class="col-lg-3 col-md-4 col-sm-12 col-12">
<%= f.check_box :skills, { multiple: true }, activity, false %>
<%= activity %>
</div>
<% end %>
</div>
</div>
</div> <!--bottom-->
</div>
In my view "show.html.erb":
<% #name_of_model.skills.each do |skill| %>
<%= skill %>
<% end %>
For posterity sake:
<div class="row">
<div class="col-md-12">
<div class="form-group">
<%= f.label "Select a Product" %><br />
<%= f.check_box(:option) %>
<%= f.label(:mug, "Option") %><br />
<%= f.check_box(:option2) %>
<%= f.label(:mousepad, "Option2") %>
</div>
</div>
</div>

Rails materialize-sass form with autocomplete and cocoon gem nested resource

I am using materialize-sass gem on a form. Using autocomplete feature to get Vendor names. other fields on the form are item names and quantity which are a nested resource. For this I am using cocoon gem
For some reason the very first time the page loads, all seems to work fine. But adding more fields does not seem to be working. I tried using drop down select and that does not show the list of items. I replaced it with another autocomplete and the main field does show however the auto complete feature does not work. Any idea what may be wrong?
Please see my code below.
purchase_orders _forms.html.erb
<div class="col s12">
<%= simple_form_for(#purchase_order) do |f| %>
<%= f.error_notification %>
<div class="row">
<div class="col s6">
<%= f.input :vendor_name, input_html: { class: 'vendor_name autocomplete' } %>
</div>
</div>
<section class="show-section">
<div class="row">
<div class="col l12"><h4>Item List</h4></div>
<div class="col s12">
<%= f.simple_fields_for :purchase_order_details do |purchase_order_detail| %>
<%= render 'purchase_order_detail_fields', :f => purchase_order_detail %>
<div class="links">
<%= link_to_add_association 'Add More', f, :purchase_order_details %>
</div>
<% end %>
</div>
</div>
</section>
<%= f.button :submit %>
<% end %>
</div>
_purchase_order_detail_fields.html.erb
<div class="nested-fields">
<div class="row">
<div class="col l6">
<%#= f.input(:item_id, collection: Ingredient.is_active, label_method: :title, value_method: :id) %>
<%= f.input :item_name, input_html: { class: 'item_name autocomplete' } %>
</div>
<div class="col l5">
<%= f.input :item_quantity %>
</div>
<div class="col l1">
<%= link_to_remove_association "delete", f, :class => "material-icons teal-text text-lighten-1" %>
</div>
<%#= f.hidden_field :item_type , :value=> params[:category_id] %>
</div>
</div>
Checking out the demo-project they use, and more specifically the init.js they use I notice two things: if you are using turbolinks you will have to do the same, and when using cocoon you will have to do something like:
$('form').on('cocoon:before-insert', function(e, insertedItem) {
$(insertedItem).find('select').material_select();
})
This is the coffee script I used to be able to populate the autocompelte
$.ajax
url: '/packing_materials/by_name.json'
dataType: 'json'
success: (my_res) ->
$ ->
$('input.packing_material_name.autocomplete').autocomplete data: my_res
$(document).on 'cocoon:before-insert', ->
$.ajax
url: '/packing_materials/by_name.json'
dataType: 'json'
success: (my_res) ->
$ ->
$('input.packing_material_name.autocomplete').autocomplete data: my_res

Nested form isn't saved

I am using the gem Cocoon for nested form and this form don't get submitted...
What am I doing wrong please? Thank u
_form.html.erb
<div class="container">
<div class="row">
<%= simple_form_for #event do |f| %>
<div class="col-xs-12">
<h3>Your event to share</h3>
<%= f.input :name, label: "Event's name" %>
<%= f.input :total_price, label: "What is the total price" %>
<h3> Add your friends to share the bill</h3>
<div id="participants">
<%= f.simple_fields_for :participants do |participant| %>
<%= render "participants_fields", f: participant %>
<% end %>
<div class="links">
<%= link_to_add_association "add a friend", f, :participants, partial: "participants_fields", class:"btn btn-primary" %>
</div>
</div>
<%= f.button :submit %>
</div>
<% end %>
</div>
</div>
_participants_fields.html.erb
<div class="nested-fields">
<%= f.input :first_name, label: "Enter your friend's first name" %>
<%= f.input :salary, label: "Enter his/her monthly pay" %>
<div class="links">
<%= link_to_remove_association "Remove this friend", f , class: "btn btn-danger btn-xs" %>
</div>
</div>
I used this tutorial to implement nested forms with cocoon gem just few hours ago. You might find it useful. https://www.youtube.com/watch?v=56xjUOAAZY8
To get a strong understanding of how to create a nested form you should also see this video on how to do it without a gem. https://www.youtube.com/watch?v=pulzZxPkgmE
I was missing this relation
has_many :participants, inverse_of: :event
I add has_many :participants, dependent: :destroy

Resources