0

I want the following on my form:

enter image description here

Degree is polymorphic partial with has_many relation to profile. This partial is called by main form - profile. By pressing 'Add a Qualification' user can add as many degrees. I've been able to attach only one degree to profile which is the last one others are all ignored. I even know why it's happening because link_to is not able to pass profile instance. so I have to create new profile in degrees_controller as you can see in my code here. Can final profile instance pick up all others above when submitting 'Create Profile'.

Kindly any help so that I can have all of the degrees attached with form, I'm stuck on this for last couple of days with all permutations and combinations from SO and google. I'm ready to change code even....any help with this will be appreciated.

Community
  • 1
  • 1
Means
  • 322
  • 2
  • 4
  • 16

2 Answers2

0

I've got this little helper to get me through these cases. Its reusable so you don't ever have to write all this stuff for different cases in the same app.

First the code:

module FormsHelper

  def data_for_field(f, subclass, options={})
    new_object = f.object.class.new.send(subclass.to_s).new(options[:attributes])
    id = new_object.object_id
    partial_path = options[:path] || new_object
    fields = f.fields_for(subclass, new_object, child_index: id) do |builder|
      render(partial_path, f: builder)
    end
    {id: id, fields: fields.gsub("\n", "")}
  end

end

Some JS magic:

$(document).on('click', '.add-fields', function(e){
  var time = new Date().getTime();
  var regexp = new RegExp($(this).data('id'), 'g');
  var content = $(this).data('fields').replace(regexp, time);
  $(target).append(content);
  e.preventDefault();
});

Now a view call:

<%= form_tag .... do |f|
    <div id="list"></div>
    <%= link_to 'Add an Organisation', '#', class: 'add-fields', data: data_for_field(f, :degrees, path: "/degrees/fields").merge(target: "#list") %></p>
<% end %>

Now create a partial where the degree fields to use.

/degrees/_fields.html.erb

<%= content_tag :div do %>
  <%= f.input :name %>
<% end %>

Now the explanation:

For data_for_field you pass: f (form), :degrees (the plural name of the associated model as a symbol), and any additional options like the path to the partial for the fields. It basically creates a scaffold of the form fields and sets them as a data attribute on the "Add Qualification" link. I've merged in a data-target which has the css ID of where to insert the new fields into the page as well.

When you click the JS event fires and replaces the id in the form fields with a timestamp so for each qualification you'll have a unique key.

I also usually have a little extra JS event for removing the fields as well.

Hope this helps.

Dan
  • 1,136
  • 10
  • 24
  • Firstly, thanks Dan. I wasn't hoping anyone will help in this as I did not get any answer to my question yesterday.To be honest, I was so dejected that I wanted to give up on this altogether. Secondly, regarding the code, how will data_for_field() be called?Now here's the link_to code.<%= link_to (fa_icon 'plus').to_s + " Add a Qualification ", new_profile_degree_path({:degreeable_id => @profile.id}, @degreeable.degrees.new), remote: true %>. Basically I'm setting degreeable to @job. – Means Feb 28 '17 at 14:15
  • Sorry - i didn't properly indent one of the code snippets which made it invisible for some reason. I've now updated the answer to include the call to data_for_field(). – Dan Mar 01 '17 at 09:05
  • I should probably also mention this automatically makes use of f.fields_for Basically you don't need to make a remote call to load in additional form fields. Let me know how you go, glad to help. – Dan Mar 01 '17 at 09:08
  • Hi Dan, much thanks. I'm out and about for couple of days but this is priority number 1. I never thought it will take so much time....vagaries of coding. I have to incorporate polymorphism-degreeable and JS part in my overall code and delete other part & see how all works in tandem. I will get back to you when I face any issues. – Means Mar 01 '17 at 11:40
  • Hi Dan, I have executed the code but unfortunately, it isn't displaying degree/_fields at all. Also I wanted ajax(remote:true) so that upon pressing buttton, the main profiles form doesn't reload. I don't know if JS is executing. Pls suggest what can be the issue. I really want this to work. – Means Mar 05 '17 at 07:13
0

This is a polymorphic nested association and can be handled on client side with javascript. So, finally for nested fields I used the plugin Numerous.js. Just follow as the steps given in the qucikstart part of link by downloading the numerous.js file from the Github and saving to assets/javascripts.

In my code,

profile.rb

class Profile < ApplicationRecord
  has_many :degrees, :as => :degreeable
  accepts_nested_attributes_for :degrees, :reject_if => :all_blank, allow_destroy: true

  belongs_to :user, :class_name => 'User', :foreign_key => 'user_id'
end

degree.rb

class Degree < ApplicationRecord
   belongs_to :degreeable, polymorphic: true
end

profiles/_form.html.erb

    <div id="fields-for-list" class="numerous">
      <%= f.fields_for :degrees, Degree.new, :child_index => 'replace_this' do |degree_form| %>
         <%= degree_form.select :level, options_for_select(Job::EDUCATION, params[:level]), include_blank: "Select Degree", :class => 'span5' %>
         <%= degree_form.text_field :description, placeholder: "Add a new Degree here..."%>
         <%= link_to 'x Remove', '#', :class => 'numerous-remove', type: 'button' %>
     <% end %>
   </div>
   <div id="list"></div>
   <%= link_to (fa_icon 'plus').to_s + 'Add a Qualification', '#', :id => 'add-to-list' %>

and finally, with strong parameters,

  def profile_params
    params.require(:profile).permit(:user_id, :first_name, :last_name, degrees_attributes:[:id, :level, :description])
  end

Note that I had already set-up degree table with 2 extra fields for polymorphic association:- "degreeable_id" & "degreeable_type" and when entering in DB, the two fields were automatically filled with newly created profile_id and 'Profile'(the name of the model which is associating polymorphically with degree).

The trick in numerous.js is creating each nested form record(here degree) with unique temporary id such as Time.now.to_i so now each degree record created/destroyed at client side will have a diff degree_attribute 'id'. Hope it helps others.

Means
  • 322
  • 2
  • 4
  • 16