12

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
RailsSon
  • 19,897
  • 31
  • 82
  • 105

2 Answers2

9

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
  • 3,765
  • 2
  • 20
  • 37
  • Actually today I would suggest to use meta tag attribute to pass variables into the page. You can read those attributes via javascript easily. – retro Nov 23 '15 at 12:12
5

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 %>
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985