Ruby on Rails - Send JavaScript variable from controller to external Javascript asset file


I am creating a website in Ruby on Rails. I have a controller action that renders a view like so:

def show
  time_left = Time.now.to_i - 3.hours.to_i
  @character = current_user.characters.find(params[:id])
  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :xml => @character }
  end
end

This is fine as it renders the show.html.erb as I like. I would like however to somehow pass time_left to the view as a Javascript variable as this value is use by a countdown JQuery plugin.

I could put a javascript block on the page in the HTML and print a instance variable out like so:

<script type="javascript"> $('#countdown').countdown('<%= @time_left =>')</script>

But I would like to keep all my JS in a external file and off the page could anyone give some advice on how to implement this?

Yes, you can!

Rewrite your JS code into function with one argument (timelimit) and put it into some external file. Then you can call the function from view and pass that @timeleft variable as JS function argument.

Short example:

#controller
@time_left = Time.now.to_i - 3.hours.to_i

.

#javascript
function count_down(time_left) {
  $('#countdown').countdown(time_left)
}

.

#view
<%=javascript_tag "count_down(#{@time_left})" -%>

javascript_tag

Example not tested, it is only idea not complete solution. Don't forget to load that JS file. You can use other JS rails helper javascript_include_tag.


retro's technique of using a function parameter is a possibility, but you have to properly escape the variable you are passing with either escape_javascript or to_json + html_safe as explained below.

However, since you want to affect external files, the best techniques will be to use gon. Another good possibility is to use data- attributes.

gon

Gem specialized for the job: https://github.com/gazay/gon

Probably the most robust solution.

Gemfile:

gem 'gon'

Controller:

gon.timeleft = 1

Layout app/views/layouts/application.html.erb:

<html>
<head>
  <meta charset="utf-8"/>
  <%= include_gon %>
  <%= javascript_include_tag 'application' %>

Asset file:

gon.timeleft === 1

data- attributes

Add values to attributes, retrieve them with JavaScript DOM operations.

Sometimes called "unobtrusive Javascript".

View head:

<%= javascript_include_tag 'application' %>

View body:

<%= content_tag 'div', '', id: 'data', data: {timeleft: '1'} %>

Asset file:

$(function() {
  parseInt($('#data').data('key1')) === 1
})

The following illustrate how escape_javascript and to_json work for you to use on top of retro's answer.

escape_javascript

Alias: j.

Works only on strings.

Escapes characters that can have special meanings in JavaScript strings, like backslash escapes, into a format suitable to put inside JavaScript string literal quotes.

Maintains html_safe status of input, so needs html_safe otherwise special HTML chars like < would get escaped into &lt;.

<% a = "\\n<" %>
<%= javascript_tag do %>
  f('<%= j(a)           %>') // === '\\n&lt;'
  f('<%= j(a).html_safe %>') // === '\\n<'
<% end %>

to_json + html_safe

Works because JSON is almost a subset of Javascript object literal notation.

Works not only on hash objects, but also on strings, arrays and integers which are converted to JSON fragments of the corresponding data type.

<% data = { key1: 'val1', key2: 'val2' } %>
<%= javascript_tag do %>
  var data = <%= data.to_json.html_safe %>
  f(data.key1) \\ === 'val1'
  f(data.key2) \\ === 'val2'
<% end %>