12

I have a Rails app, I recently updated to 5.0.0.RC1. Most of the transition went smooth, but I'm having some trouble with the new Turbolinks. In my app I for example use this gem:

gem 'chosen-rails'

My application.js file looks like this:

//= require jquery
//= require jquery.turbolinks
//= require jquery_ujs
//= require best_in_place
//= require tether
//= require bootstrap
//= require chosen-jquery
//= require_tree .
//= require turbo links

When I click on a link and render a view my chosen-query (best_in_place doesn't work as well) doesn't work in the initial load, but if I make a hard refresh of the page then it works. Below is an image of the result I'm getting:

How it looks now

And here is an image of how I want it to look:

enter image description here

Again, the expected look works if I make a hard refresh of the page, but not after a regular redirect_to ...

The code for my dropdown looks like this:

= select_tag :screen, options_from_collection_for_select(@screens, "id",  "name"), id: "screen-selection", prompt: "Jump to screen", class: 'form-control  chosen-select', style: "max-width: 250px !important"

After a redirect_to it results in the following HTML:

<select name="screen" id="screen-selection" class="form-control chosen-select"  style="max-width: 250px !important">[...]</select>

... and after a hard page reload I get this:

<select name="screen" id="screen-selection" class="form-control chosen-select"  style="max-width: 250px !important; display: none;">[...]</select>

<div class="chosen-container chosen-container-single" style="width: 190px;" title="" id="screen_selection_chosen"><a class="chosen-single"><span>Jump to screen</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off"></div><ul class="chosen-results"><li class="active-result result-selected" data-option-array-index="0" style="">Jump to screen</li><li class="active-result" data-option-array-index="1" style="">tests</li></ul></div></div>

In a .coffee file I try to initialise chosen like this:

# enable chosen js
$('#screen-selection').chosen({
  width: '190px'
})

Any ideas on what I'm doing wrong?

Anders
  • 2,903
  • 7
  • 58
  • 114

7 Answers7

29

With turbolinks the javascript is only loaded once. On the hard refresh chosen works because the entire page is re-rendered. Once you click a link, turbolinks hijacks the request and turns it into an ajax one (instead of a full page refresh). Once the request comes in, turbolinks only replaces the body of the page, leaving the JS in the same state it was.

To fix this you will need to wrap your chosen initializer in the turbolinks:load event like so

$(document).on('turbolinks:load', function() {
  $('#screen-selection').chosen({
    width: '190px'
  })
})

For more information, see https://github.com/turbolinks/turbolinks#installing-javascript-behavior

patkoperwas
  • 1,341
  • 10
  • 14
  • 2
    But there is a big BUT: turbolinks stores the page in cache so you should make sure your javascript code is idempotent, otherwise you can end up having several selects on the page (or, in other cases, several event handlers attached to an element). For more info, pls read https://www.crossplatform.net/rails/article/should-you-disable-turbolinks/idempotent-transformation/ – Marek Příhoda Aug 09 '16 at 15:03
  • The several event handlers part isn't too much to worry about unless one is attaching handlers to the document. Otherwise at least in Turbolinks 5 the is cloned and reinstated without any associated handlers etc... surviving. – Brendon Muir Oct 10 '16 at 07:51
8

Here is my solution with Turbolinks 5 and jQuery:

  1. install gem 'jquery-turbolinks'

  2. add this .coffee file to your app: https://github.com/turbolinks/turbolinks/blob/master/src/turbolinks/compatibility.coffee

  3. name it turbolinks-compatibility.coffee

  4. at application.js

//=require jquery
//=require jquery_ujs
//=require jquery.turbolinks

//=require turbolinks
//=require turbolinks-compatibility

Pascal
  • 1,158
  • 17
  • 20
5

I got a serious headache to find a way to make all my javascript work. First i try the solution with the gem jquery.turbolinks by following this video: How to upgrade to turbolinks 5 but i got lightbox for showing the images in the same page not working properly. So before just disable Turbolinks once and for all, i take more time on the github page and i try this:

<div data-turbolinks="false">
  <a href="/">Disabled</a>
</div>

You need to put that on each link where the javascript need a page reload to work.

Maybe its not the best solution, but that make my headache less painful.

VendezTrouvez
  • 121
  • 2
  • 2
0

Turbolinks and jquery are kinda headache. Instead of calling an action on document ready, on page:load should work better, 'cause with turbolinks, it doesn't reload the entire documment when you browse the website. Something like this may work:

$(document).on('page:load', function() {
  $('#screen-selection').chosen({
    width: '190px'
  })
}
Ronan Lopes
  • 3,320
  • 4
  • 25
  • 51
  • 4
    The question is about the "new Turbolinks" (i.e., Turbolinks 5), which actually breaks apps which use events other than 'turbolinks:load'. So the approach you suggest no longer works with Rails and Turbolinks 5. – user3670743 Jul 16 '16 at 19:11
0

I tried to solve this myself and found a solution that works for me.

My problem was that i had a forum with a "quote" functionality for comments. With turbolinks 5 i got several event listeners and ended up with up to four quotes from the same comment in my editor.

I read upon idempotence and found a solution here: How to make jQuery `bind` or `on` event handlers idempotent

I created (copied really) a global function that i use in my globals.coffee file for events.

$.fn.event = (name, fn) ->
  @unbind(name).bind name, fn

And it works.

My functions looks like this:

$('.quote').event 'click', (event) ->
 do something 

All creds for this goes to: Pointy who delivered the solution.

Link to turbolinks and idempotence

Community
  • 1
  • 1
Björn Grunde
  • 55
  • 1
  • 9
0

Here's what I do.. Turbolinks5 to prevent loaded multiple times.

var ready = function () {

   if ($.turboAlreadyLoaded) {

      $('#screen-selection').chosen({
        width: '190px'
      })
   }
$.turboAlreadyLoaded = true;

}
$(document).ready(ready);
$(document).on("turbolinks:load", ready);
Marcelo Austria
  • 861
  • 8
  • 16
0

I tried to solve this myself and found a solution that works for me. I also hope Helpfully for others.

(function () {
    $('#screen-selection').chosen({
        width: '190px'
    })
})();
Sadegh19B
  • 1
  • 1