0

So, the thing that i want to do, is to have a button, to add more fields into a form. Let's say i have this:

<div class="row">
      <div class="form-group string required">
        <div class="cep_filter">
          <div class="col-md-3 traffic-columns">
            <%= f.input :condition, collection: @traffic_columns, label: "Monitor (*)" %>
          </div>
          <div class="col-md-1">
            <%= f.input :operator, collection: @operator, label: "Op (*)" %>
          </div>
          <div class="col-md-2">
            <%= f.input :filter, label: "Value (*)",input_html: {type: "text" } %>
          </div>
        </div>
        <div class="col-md-3">
          <%= f.input :window, collection: @window, label: "Window Type", input_html: {type: "text"} %>
        </div>
        <div class="col-md-3">
          <%= f.input :window2, label: "Window Value", input_html: {type: "text"} %>
        </div>
      </div>
    </div>

Then, suppose i have a button like this:

<%= link_to content_tag(:i, nil, class: "fa fa-plus"),
             '', style: 'color: green', class: "btn btn-small add-new-condition", 'data-backdrop' => "static" %>

What i want to do, is that when you click that button, you call this:

$(document).on("click", ".add-new-condition", function(e) {
  var html =
  "<div class='col-md-3 traffic-columns'> \
    <%= f.input :new_param, collection: @traffic_columns, label: 'Monitor (*)' %>\
  </div>
  "
});

and then append that html to some div element. From what i've read, its not possible to inject ruby from javascript. Its something related to one being server side and the other client side. I already try it and the ruby code appears as a string. I could build those new fields by using the pure html, like and so, but the important thing here, is that i want to add new fields that later i can access in a controller, for example the new field, with params[:new_param].

Is there a way to add new fields only with html so i can access later the entered value in my rails controller? Thank you.

3
  • 1
    You can do it with an ajax call. Using ajax, the client side can fetch the desired HTML element string from the server and you can then insert the received string into the DOM where ever you need it. Commented Jun 18, 2020 at 16:02
  • Probably easiest thing would be to load the traffic-columns field onto the page but make it initially hidden. Then just toggle the visibility using JS. Your current approach won't work because the browser can't execute Ruby, so it won't know what to do with the whole <%= and f.input. Remember that when you send the view HTML from your server, it is going through a Ruby/ERB preprocessor step, and that doesn't happen here Commented Jun 18, 2020 at 16:32
  • Anytime you hit submit, all of the elements that have names should be put into params[:name] whenever you submit the form. I assume you wouldn't need the data in your controller before submission too. Commented Jun 18, 2020 at 17:19

2 Answers 2

1

Have a look at the Working with JavaScript in Rails guide, specifically the section about how to respond from your Rails server with some JavaScript that then gets evaluated by the client ("rails-ujs").

This gives you all the powers you need (and can replace quite a lot what people nowadays use entire JS frameworks for, btw)

All you need is link_to ... remote: true to a path that is handled by a controller action that can respond_to ... format.js. Then you put a something.js.erb file into the correct place in your app/views folder and Rails does the rest for you.

Have a close look at the full guide to get the full picture and comment below if you still have questions. I'd be happy to extend my answer!

Sign up to request clarification or add additional context in comments.

1 Comment

This answer is what i needed, although i like the one from below, for me this is the one that will make my life easier. Thanks!
1

You’ve VERY ALMOST succeeded already!

Are you getting the literal string <%= f.input :new_param, collection: @traffic_columns, label: 'Monitor (*)' %> injected into your div, whereas what you want would look more like <input type="text" />?

That's because the ruby server is what will convert that ERB code into HTML.

It looks like you're already creating an input field in your ERB with <%= f.input :condition, collection: @traffic_columns, label: "Monitor (*)" %>. Give the wrapping div an id so the form contains this as the first field:

<div class="col-md-3 traffic-columns" id="duplicate_and_inject_this">
            <%= f.input :condition, collection: @traffic_columns, label: "Monitor (*)" %>
          </div>

Then, on the click event, clone that div and append it wherever you want.

$(".add-new-condition").click(function (){
  var new_input = $("#duplicate_and_inject_this").clone();
  new_input.appendTo( "#new_inputs_wrapper" );
})

This way, ruby is rendering your input fields for you and JS will just duplicate that input field as many times as you click on the button.

If you want to start with zero instances of the input, just hide it as someone else has mentioned, and then make it visible after you've cloned it.

You can alter the clone in other ways, to update the new name attribute or change it's value before you append it to your form.

If, as has otherwise been suggested, you were to use link_to remote You’d have to make a server call every time you add a field. Cloning the HTML and injecting it with JS will be much faster.

3 Comments

Did not put this as an answer but was really useful also, since i want to add more fields inside my partial. Only problem with this, is that you are cloning a field with the same parameter name (:condition) in this case, so later in the controller you would have multiple values inside the params[:condition].
You can change the name parameter in the cloned element, before you append it. new_input.attr(‘name’, ‘new_name_param’); should do what you need.
Although I totally understand if you want the rails controller to have responsibility for naming the params. Just adding the JS solution for future readers or if you change your mind.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.