Search code examples
javascriptruby-on-railspolymerturbolinks

Polymer JS conflicts with Rails Turbolinks


Trying to use PolymerJs with a Ruby on Rails project with Turbolinks enabled. The initial loading of Polymer components works,but when I click on a link the new page renders, but my Polymer components are not reinitialized. Instead they look as if no JS has been run on them at all.

I am loading polymer components and platform in the header, so they are not loaded with the 'page changes' (this is how turbolinks works - it only reloads the body).

I seem to be able to 'fake it' by doing the following assuming I wanted to 'refresh' my-custom-element and the core-ajax tags on my page

custom_elements = document.querySelectorAll("my-custom-element, core-ajax ") 
for element in custom_elements 
  element.parentNode.replaceChild(element.cloneNode(), element) 

I am guessing there is a more idiomatic way to do it though. Also tried to call the Polymer() call inside the element again with out success. It seems like it fires properly, but there is no attachment of the shadowDom or any other activity indicating Polymer has attempted to reattach my elements. I also tried using preventDispose, but that does not seem to work as an entirely new body is being loaded from the server.

How can I tell Polymer/Platform to reinitialize all elements on my page? What events should I be calling?

Or if that cant be done, how do a initialize just a given polymer element.


Solution

  • Written for Turbolinks 4. Untested in Turbolinks 5

    Turbolinks replaces the Document Body after fetching it via replaceChild

    https://github.com/rails/turbolinks/blob/master/lib/assets/javascripts/turbolinks.js.coffee#L97

    document.documentElement.replaceChild(body, document.body);
    

    This is not trigger the 'upgrading of elements' as it seems the Observers do not fire in this case.

    I have come up with 2 different solutions to get around this.

    Importing the entire body

    $(document).on "page:change", ->
       document.documentElement.replaceChild(document.importNode(document.body, true), document.body);
    

    Importing just the specific registered Polymer Elements

    $(document).on "page:change", ->
      for polymerElement in Polymer.elements
        for domElement in document.body.querySelectorAll(polymerElement.name)
          domElement.parentNode.replaceChild(document.importNode(domElement, true), domElement);
    

    Update

    This seems to align with the officially endorsed way to handle this situation

    https://groups.google.com/forum/#!msg/polymer-dev/WaWbepYW97Q/XE3SQTyvyCUJ

    Also a good use case for why this step is required

    To see why you might want to avoid changing type when adopting, consider an element born in an that gets moved into the main document. You probably wouldn't want to go back through the lifecycle just because it moved to a new document...