Search code examples
javascriptruby-on-railsdompartialsujs

Rails: Re-render partial after form submit and bind to dom


I know this has been asked and re-asked, but I'm just not able to nail it.

I have a view that renders a form from a partial

.myform
  = render "someform"

Here is the form

= simple_form @object, remote: true do |f|
  = f.input :field1
  = f.input :name, as: :autocomplete
  = f.submit

Not doing any fancy rendering in the controller, it just rendering the js view as the form was submitting using remote: true

So it calls object.js.haml

:plain
  $("form.new_object").html("#{escape_javascript(render(:partial => '/objects/someform'))}");

Essentially, I'm just replacing the form with a new object.

The trouble is, and you JS-heads will see where I'm going with this, it's no longer tied to the DOM, which means other javascript events are not firing, in this case, typeahead (which is using the simple form input called autocomplete).

So, how can I re-render the someform partial and making sure new input's are tied to js typeahead events?


Solution

  • One way to handle binding event handlers in this wild and crazy POST ajax world is to use delegated events.

    The whole idea is than instead of tacking a handler onto each little element you delegate to containers or even the document itself.

    $('#add').click(function(){
      $("#playground").append("<button>Test me too!</button>")
    });
    
    // This will only fire when you click the button that was present
    // when the handlers where bound
    $("#playground button").click(function(){
      console.log("I'm bound to an element!");
    });
    
    // This trickles down to every element that matches the selector
    $("#playground").on('click', 'button', function(){
      console.log("I'm delegated!");
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    
    <div id="playground">
      <button>Test</button>
    </div>
    
    <button id="add">Add a new button</button>

    But since typeahead does not let you delegate in that way what you can do is attach the typeahead handler before you attach the elements to the DOM.

    var obj = $("#{escape_javascript(render(:partial => '/objects/someform'))}");
    obj.find('.typeahead').typeahead({
     // ...
    });
    
    $("form.new_object");