11

I am trying to implement nested models, here is the route file entry:

resources :projects do
  resources :instances
end

Following is the snippet for project controller:

# GET /projects/new
def new
  @project = Project.new
  @project.instances.build
end

and project's form view:

<%= simple_form_for(@project) do |f| %>
  ...
  <%= label_tag :instance_count, "Instance Count" %>
  <%= select_tag :instance_count, options_for_select([0, 1, 2, 3, 4, 5], 0) %>
  ...
<% end %>

Now when I change the number of instance count, I need to display instance fields those many times below the above form. Here is the partial code for that:

<%= form.simple_fields_for :instances do |i| %>
  ...  
<% end %>

Basically I need to call <%= render 'instances/form', form: f %> from project's javascript file. It should work like link with remote: true option. But in this case there is no link, but on change event the form need to be displayed. How should I implement this?

Rajkaran Mishra
  • 4,532
  • 2
  • 36
  • 61
  • when you click on `@project[:instance_count]` and change the number, you want to display a different number of `instances` input fields? Does this require AJAX? maybe it is just enough listening to that click event on that div to append a new div on that page and when you submit the form, make sure the submit works for those fields .... I am thinking of posting an answer.. Adding input fields it is kind of tricky because the form has a `token` that avoid CSRF, also the submit needs to be performed maybe with `Javascript` and you would need to create a jquery `.post()` request – Fabrizio Bertoglio Oct 20 '17 at 14:12
  • Your trigger hear is the clicking on the `<%= select_tag :instance_count, options_for_select([0, 1, 2, 3, 4, 5], 0) %>` so `$('#select_tab').click( // add input div to the page depending on the div .value() result );`, there is no server interaction. Then you need to do the `post` request in the javascript file with this function https://api.jquery.com/jQuery.post/ – Fabrizio Bertoglio Oct 20 '17 at 14:20

5 Answers5

1

I suggest you to use https://github.com/nathanvda/cocoon

Or you can use similar aproach: render partial in initial form (with display:none), then remove and save partial fields with js and clone them to form when selector is hit.

kolas
  • 754
  • 5
  • 16
0

You have to once call serverside code because instances/form contains code that can only be rendered on serverside.
First you have to do ajax call(ex. instance_new_path) and then you have to render the form on that view(instance_new.js.erb).

example .js.erb

$("#new_form").html("<%= escape_javascript(render partial: 'instances/form' ) %>");
My Favorite Bear
  • 114
  • 1
  • 1
  • 10
0

Standard is to make a partial called app/views/instances/_instance_fields.html.erb. Then you can just load it into your form and make it hidden.

<%= simple_form_for(@project) do |f| %>
  <%= render 'instances/_instance_fields %>
<% end %>

Cover _instance_fields partial with some kind of container, like <fieldset class='instance_fields' style='display:none'>. Also, you shouldn't use form object there, just go with text_field_tag/checkbox_tag inputs there. Then when you need to add more instances you just copy this hidden snippets as much times, as you need and setup proper names for inputs (to be accaptable for accepts_nested_attributes_for).

Ping me to provide more details and assistance. This is an approach that was used in real project. Fire ajax call every time you need to add more instances is not optimized at all.

AntonTkachov
  • 1,784
  • 1
  • 10
  • 29
  • You need to store this piece in some hidden template and copy this template for each new instances. After copy you need to setup proper names with help of javascript. Right some specific notes what's not working in approach I gave you. You should not user `from` attribute. It's absolutely useless in a case of template, because you still will need to handle input `name` attributes manually in javascript – AntonTkachov Oct 13 '17 at 17:58
0

Create a .js file, and load it inside projects/new.html.erb, which will execute whenever there is a change in select value and creates a post request to instances/new controller which will render instances/new.js.erb every time it is hit.

# GET /instances/new
def new
  @f = Instance.new
end

instances/new.js.erb

$('#instance-form-wrapper').append(<% escape_javascript(render 'instances/form', form: @f) %>

load.js

$(document).on('change', 'select#some-id-name', function(){
  var v = $(this).val();
  $('#instance-form-wrapper').html('') ;
  while(v--) $.post('/instances/');
}) 

Though you should not use like this. Rather load one instance field already in your code, keep it hidden, as you already have instance available in your project. Also, you do not need any new data every time when you render it. Simply on selection of value 1 you can show your instance field and if the value is > 1 you can use clone to copy it further.

Shovon
  • 71
  • 3
0

Just use gem https://github.com/nathanvda/cocoon for the nested forms

itsnikolay
  • 17,415
  • 4
  • 65
  • 64