Ruby on Rails 5 - Turbolink 3 did not work correctly - ruby-on-rails

I am using the Ruby on Rails 5 and use turbo-link as shown below:
<%= javascript_include_tag 'custom-plugins', 'data-turbolinks-track' => true %>
It is working fine with Ruby On Rails 4 but have issue with Ruby on Rails version 5. Once I click on back the js/css do not loaded correctly.
Any help will be appreciated.

As Refereed to Rails 5 Awesome features
Turbolinks has been part of Rails since version 4, probably one of the features that people hate it or love; there is no middle ground here.
With Rails 5 we will be receiving a new version that, with the help of HTML5 custom data attributes, we will expect better speed and rendering in our Rails applications.
The most significance change in this new version is the Partial Replacement feature. From the client side, we will be able to tell Turbolinks what content do we need to change/replace and what we don’t.
Turbolinks will look for HTML5 custom attributes and to decide the replacement strategy in our .
To trigger a replacement in the client side we could use or to update our . The difference between and is that the first one will issue a to the server to obtain the HTML that must be used to replace our while expects from us the HTML that should be used for its operation.
With both functions, we can pass a hash with an or an array of of HTML elements to or.
Action Result
Turbolinks.visit(url, { change: ['entries'] }) Will replace any element with custom attribute and any element with its id listed in change.
Turbolinks.visit(url) Will keep only elements with custom attribute and replace everything.
Turbolinks.visit(url, { keep: ['flash'] }) Will keep only elements with custom attribute and any element with its id listed in keep, everything else will be replaced.
Turbolinks.visit(url, { flush: true }) Will replace everything
We can trigger the same functionality from the server-side with and , both can receive , and as options but can also receive with or to force a redirect with or without Turbolinks.
Whether you like Turbolinks or not, this might be a good time to try out and find out if it could be a good fit somewhere in your application.

It is a common occurrence in for turbolinks with js. Turbolinks helps load a particular page a lot faster. But what it also does is stops the js from functioning sometimes. So while loading this particular page use this line
<%= link_to "example_page", example_page_path, :"data-no-turbolink" => true %>
or
write this in your layout
<body <%= "data-no-turbolinks='true'".html_safe if controller_name=="example_controller" && action_name=="example_page" %>>
to stop turbolink from working in this particular page.

Related

Rails Datatables with Ajax not working with Turbolink

So I have a new Rails 4 App using foundation and I started to integrate DataTables today and ran into one minor setback after setting it up to page using ajax calls.
When I click a link from one page (home in this instance) that sends me to a page that contains the datatables, the tables render without any entries or search box or pagination etc. However, if I refresh my browser, the page will fully refresh and the ajax call is made and the the table populates correctly.
After looking at the network traffic, I'm seeing that after clicking the link the response is 304: Not Modified. Since none of the other requests for the JS and CSS and etc, I'm assuming that the JS doesn't reload and make the proper ('#tasks').dataTable({...}) call.
Also, one thing to note is that the table is residing within a partial 'tasks_index.html.haml'.
One thing I did remember however was that I was still using the Turbolinks gem. I tried disabling it to see if that would fix my problem and surprisingly enough, it did.
So what would cause Turbolinks to prevent normal javascript to load on the page? Is there any way to force turbolinks to always load certain pages? Am I better off not even using Turbolinks?
I guess you are using the $(document).ready event to initialize the datatables. When using turbolinks when you click a link this event won't fire because turbolinks loads the page itself. So you should use the document page:change event instead. See here: http://guides.rubyonrails.org/working_with_javascript_in_rails.html#page-change-events
Just like lunr said, you have to put $(document).on "turbolinks:load", -> in your .coffee file
I successfully used the following CoffeeScript code to make DataTables work correctly on Rails 5 / Turbolinks 5.
If you use this script, you will no longer have the problem where the DataTables wrapper reloads after you hit the back button on your browser.
Please note that I also am using the turbolinks-compatibility script.
I'm hopeful this will help someone else who is Googling / struggling with this like I was.
TurboLinks is great for speed ... but it makes loading JavaScript a pain.
$(document).on 'turbolinks:load', ->
tableElementIds = [
'### TABLE ID HERE ###'
]
i = 0
while i < tableElementIds.length
tableElementId = tableElementIds[i]
if $.isEmptyObject($.find(tableElementId))
i++
continue
table = undefined
if $.fn.DataTable.isDataTable(tableElementId)
table = $(tableElementId).DataTable()
else
table = $(tableElementId).DataTable(### OPTIONS HERE ###)
document.addEventListener 'turbolinks:before-cache', ->
table.destroy()
return
i++
return

Facebook and Twitter widgets disappear on navigation (Rails)

I have a fb-like-box and a twitter-timeline, the official facebook page and twitter widgets. However, on actual navigation (the links in the sidebar do an ajax load), for example on clicking the logo, these two widgets vanish into thin air.
On refreshing the page, the widgets once again appear as expected.
<div id="social_bar">
<div id="facebook_like_box">
<div class="fb-like-box" data-href="https://www.facebook.com/Gx.Edg" data-width="234" data-height="500" data-colorscheme="light" data-show-faces="true" data-header="true" data-stream="false" data-show-border="true"></div>
</div>
<a class="twitter-timeline" href="https://twitter.com/gxEDGE" data-widget-id="308092518074040321"></a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</div>
The root cause is how Turbolinks interacts with header scripts. #peteykun offers the correct solution in his answer, though it is out of date and lacks an elaborate explanation. At the time of this writing, Rails 5 uses Turbolinks 5.
When you visit a page on your Rails site for the first time (or refresh the page), the page is fetched normally without interference from Turbolinks. The expected browser events fire, the JavaScript executes, and everything renders properly. The trouble starts when you navigate from that page to another page on the same domain (i.e. clicking a link). When that happens, Turbolinks intercepts the new page request and makes a slightly different request that is faster. Glossing over the details, the end result is that expected browser events do not fire as they normally would. No events, no JavaScript. No JavaScript, no rendering. Per the Turbolinks GitHub:
You may be used to installing JavaScript behavior in response to the
window.onload, DOMContentLoaded, or jQuery ready events. With
Turbolinks, these events will fire only in response to the initial
page load—not after any subsequent page changes.
The best and simplest solution is to listen for Turbolink's events instead of the standard browser events.
Fortunately, there's a website authored by user #reed of GitHub which is dedicated to rewriting a wide variety of commonly-used JavaScripts to be compatible with Turbolinks. Unfortunately, the solutions currently on the site were written for an earlier version of Turbolinks and will not work out-of-the-box. Assuming they haven't updated the site yet, you'll need to make some changes.
First, change all event bindings of the form page:* to turbolinks:*. The event namespaces were changed in Turbolinks 5. This is the only change required to get #reed's Twitter coffeescript (Solution #2) to work. As for the Facebook coffeescript, there's no such thing as a turbolinks:fetch or turbolinks:change event, but that's okay. Read on for a more elegant solution.
Reed's Facebook coffeescript uses Javascript to transfer the #fb-root element from the body of the old page to the new one. As I understand it, this is done to preserve the Facebook functionality. Turbolinks 5 has a new feature that allows you to mark a page element as permanent. User #pomartel mentioned this here, and I believe this is a much more elegant solution. Simply add the data-turbolinks-permanent attribute to the #fb-root div, and we remove the need for the saveFacebookRoot and restoreFacebookRoot functions.
The complete Facebook solution is below. In the <body>:
<body>
<div id="fb-root" data-turbolinks-permanent></div>
In the <head>:
<%= javascript_include_tag 'facebook_sdk', 'data-turbolinks-track': 'reload' %>
And the facebook_sdk.coffee file, which belongs in ./app/assets/javascripts:
$ ->
loadFacebookSDK()
bindFacebookEvents() unless window.fbEventsBound
bindFacebookEvents = ->
$(document)
.on('turbolinks:load', ->
FB?.XFBML.parse()
)
#fbEventsBound = true
loadFacebookSDK = ->
window.fbAsyncInit = initializeFacebookSDK
$.getScript("//connect.facebook.net/en_US/sdk.js")
initializeFacebookSDK = ->
FB.init
appId : 'YOUR-APP-ID-HERE'
status : true
cookie : true
xfbml : true
autoLogAppEvents : true
version: 'v2.11'
Don't forget to add the Facebook and Twitter coffeescripts to the precompiled asset pipeline!
The problem was a ruby gem that is bundled by default, called Turbolinks.
The solution is found in the Turbolinks Compatibility Project:
Facebook Like Box
Twitter Widget

mixing clientside javascript template with rails logic for backbone.js

I've worked a while now with Backbone.js, and one of the things I nowadays run into is; Sometimes you need to have serverside logic into a .eco.jst template
For example
an i18n translation (currently look at i18n.js gem for this)
a path a route without hardcoding it (somemodel_path(somemodel))
authorisation (for example, show a delete button if the user can destroy this model). Atm I solve this by passing in some rights object in the json that gets filled in.
Rendering a html helper like simple_form or S3_file_uploader (atm I solve this with rendering it serverside, and put the display on none)
As you know, .eco get parsed by node.js, so I can't call ruby in the eco files. Most of these problems I solve by basicly creating a "data" object in the head. Similar to this:
window.data = {
some_translation = "<%= t('cool') %>",
<%= "can_destoy_model = true," if can?('destroy', Model) %>
post_edit_link = "<%= post_path(#post) %>
}
Besides this being bulky (this is just an example, normally this would be more ordened or I add a html5 data attribute to some dom element), It's time consuming, sometimes you have to recreate complete business logic which otherwise would be a oneliner in rails (take for example the S3_file_uploader, which requires encoded amazon policyfile and a token)
What are your thoughts about this? Should I perhaps not use .eco (although I like templates in seperate files instead of poluting the view). Would I able to use serverside logic if I for instance used mustache or handlebars and which gem would you recommend if so?
My experience with Backbone.js is kind of limited, but I've managed to setup an environment with logic-less templates using the following gems:
handlebars_assets
haml_assets
And a bunch of other stuff, even a mini-framework I'm currently working on (you can find it here)
I picked this approach for building Single Page Applications using Backbone.
Basically, the haml_assets gem provides sprockets with the ability to parse .haml files, this is not needed but I love HAML syntax. The handlebars_assets gem provides means to parse Handlebars templates, both on the server-side and the client-side. You can use Ruby code inside the templates and you would solve both the i18n and the path methods problems you mentioned.
I've found these tools to be excellent to help DRY an application's templates, and it can really save you from adding logic inside templates. If you use Backbone Views to take, for example, decisions on whether to show a delete button or not, you can keep the logic inside the Backbone View, and use that logic to render the proper Handlebars template (or partial).
Using your example:
Coffeescript:
class ProjectShowView extends Backbone.View
template: (context) -> HandlebarsTemplates['projects/show'](context)
deleteButtonTemplate: (context) -> HandlebarsTemplates['projects/shared/delete_button'](context)
render: (canDelete = false) ->
#$el.html(#template(#model.toJSON()))
#$('.delete_button_container').append(#deleteButtonTemplate()) if canDelete
#
The example is quite primitive and basic, but can hopefully point in the right direction. I hope it helps!

rails 3 is printing html tags to screen instead of rendering them

I'm using vhochstein's fork of active_scaffold, which runs quite nicely on rails 3, except for a few small bugs - http://github.com/vhochstein/active_scaffold.
In rails 2.3, the following code disables a link:
return "<a class='disabled'>#{text}</a>" unless authorized
But in Rails 3, it causes the escaped html tags to be printed out instead as in the following photo:
How can I make the content of this return statement render the way it should in rails 3?
The code above, is from the list_column_helpers.rb file in vendor/plugins/active_scaffold/helpers/
UPDATE:
Floatless fixed this by suggesting to add .html_safe to the code.
I have since found that the folowing change also needs to be made as there's more than one bit of code that is respondible for disabling action links in active_Scaffold:
In /plugins/active_scaffold/frontends/default/views/_list_actions.html.erb change:
<%= record.authorized_for?(:crud_type => etc etc etc -%>
By making it use "raw"
i.e.
<%= raw record.authorized_for?(:crud_type => etc etc etc -%>
Anyway, thanks to floatless and hopefully mr hochstein will be able to use this stuff.
Try this:
return "<a class='disabled'>#{text}</a>".html_safe unless authorized

How Does Rails 3's "data-method='delete'" Degrade Gracefully?

Rails 3 does some cool stuff to make Javascript unobtrusive, so they've done things like this:
= link_to "Logout", user_session_path, :method => :delete
..converts to
Logout
But it just occurred to me.. When I turn off javascript the method isn't DELETE anymore, it's GET as expected. So are there plans to, or is there some way to, allow these data- attributes to degrade gracefully, so that link still is a DELETE request?
The change they made in Rails 3 with these data- attributes wasn't about graceful degradation, it was about unobtrusive JavaScript.
In Rails 2, specifying :method => :delete on a link would generate a whole bunch of inline JavaScript that would create a form with a hidden input and then submit the form. That was the same as it is now: turn off JavaScript and it defaults to a GET request. As such, supporting the case of no JavaScript is the same as it was before.
One option is to use a form/button instead of a link so you can include the method as a hidden field, much like the Rails 2 JavaScript does. Another option is to have the GET version take you to an intermediate page which in turn has the form/button.
The benefit of the new approach is that it's unobtrusive. The JavaScript for changing the HTTP verb exists in an external file and uses the data- attributes to determine which elements it should be attached to.
Rather than using the link_to method -- which would require you use JavaScript to ensure that the HTTP method is DELETE -- use the button_to method, which will create a form with a hidden input element which tells Rails to treat the HTTP method as DELETE rather than POST. If necessary, you can then use CSS to style the button in the form so that it looks like a link.
The only chance you have is define a form. A link can't be a POST with _method="delete" without Javascript or by form.
It is not possible without javascript.
I make a small jQuery plugin for converting data-method link attribute to pseudo hidden forms (used in laravel project for example).
If you want to use it : https://github.com/Ifnot/RestfulizerJs

Resources