Rails: access controller instance variable in CoffeeScript or JavaScript asset file

27,960

Solution 1

a couple of ways I have done this in the past

put the data in hidden fields, access the data in js/coffee

# single value
<%= hidden_field_tag "foo_name", @foo.name, { :id => "foo-name" } %>
$('#foo-name').val();

# when the 'value' has multiple attributes
<%= hidden_field_tag "foo", @foo.id, { :id => "foo", "data-first-name" => @foo.first_name, "data-last-name" => @foo.last_name } %>
$foo = $('#foo')
console.log $foo.val()
console.log $foo.data("firstName")
console.log $foo.data("lastName")

another option: load data into js data structure in erb, access it from js/coffee

<% content_for(:head) do %>
    <script>
    window.App = window.App || {};
    window.App.Data = window.App.Data || {};
    window.App.Data.fooList = [
        <% @list.each do |foo| %>
            <%= foo.to_json %>,
        <% end %>
    ];
    </script>
<% end %>


# coffee
for foo in window.App.Data.fooList
    console.log "#{foo.id}, #{foo.first_name} #{foo.last_name}"

I am not a big fan of constructing javascript data from ruby in erb like this, something about it just feels wrong - it can be effective though

and another option: make an ajax call and get the data on-demand from the server

I am also interested in other ideas and approaches

Solution 2

There is a really nice rail cast and quite recent (feb. 2012) about this specific topic: #324 Passing Data to JavaScript

It shows 3 ways: a script tag, a data attribute, and the Gon gem. I think house covered all the available techniques. I would only mention that using an AJAX call is best used when you have a large volume of data, dynamic data or combination of both.

Solution 3

Rather than use a hidden field I chose to add a data attribute to the container div which jquery can pick up.

<div class="searchResults" data-query="<%= @q %>"></div>

then the jquery to access it

url: "/search/get_results?search[q]=" + $(".searchResults").data("query") + "&page=" + p

I feel this is the cleanest way to pass data to javascript. After having found no way to pass a variable to a coffee script file with the rails asset pipeline from a controller. This is the method I now use. Can't wait till someone does set up the controller way with rails that will be the best.

Solution 4

In the controller:

@foo_attr = { "data-foo-1" => 1, "data-foo-2" => 2 }

In the view (HAML):

#foo{@foo_attr}

In the CoffeeScript asset:

$("#foo").data("foo-1")
$("#foo").data("foo-2")

Solution 5

In situations where your javascript data gets out of hand, using the gon gem is still the preferred way to go in rails, even in 2015. After setting up gon, you are able to pass data to your javascript files by simply assigning the data to the gon object in rails.

(Gemfile)
gem 'gon'

(controller) 
def index 
  gon.products = Product.all 

(layouts) 
<%= include_gon %> 

(public/javascripts/your_js_can_be_here.js) 
alert(gon.products[0]['id'); 

(html source automatically produced) 
<script> 
  window.gon = {}; 
  gon.products = [{"created_at":"2015", "updated_at":"2015, "id":1, "etc":"etc"}];

You can read more verbose implementation details on Gon or the two other rails-javascript channels from Ryan Bate's screencast.
http://railscasts.com/episodes/324-passing-data-to-javascript

Share:
27,960
Safa Alai
Author by

Safa Alai

Updated on February 19, 2020

Comments

  • Safa Alai
    Safa Alai about 4 years

    In Rails 3.1 it is not possible to access controller instance variables in an asset js.erb or coffee.erb file using syntax such as <%= @foo %>, where @foo is set in the controller. So then the question is what are the best ways for passing controller variables to CoffeeScript or JavaScript assets.

    This question has kind of been asked in multiple convoluted forms on the forum, but my point in asking it again is to have a place where all recommendations are gathered together, and the code supplied is simple and readable. Also note that I'm specifically referring to assets and not view response files.

  • BradGreens
    BradGreens almost 11 years
    If you need to run loops and construct json you could use a before_filter which runs a method to construct a single JSON object and assign it to an instance variable. Then you simply output the one instance variable in your view's Javascript assignment. I find this better than ajax because it's one less HTTP request.
  • Max
    Max almost 11 years
    Rudolph, open the RailsCast and it also shows code examples. Better examples than I could ever give :)
  • rudolph9
    rudolph9 almost 11 years
    I opened, I saw, but answers are supposed to be self contained with all pertinent example code (i.e. not require you to access another page in order to get pertinent information)...
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com over 9 years
    The techniques mentioned are the same as those for passing variables to Js in views: stackoverflow.com/questions/2464966/…
  • Ninjaxor
    Ninjaxor about 9 years
    This was the best answer. I added an answer that included examples using gon, but my edit approval rating is so low I just reposted it as an answer.
  • Daniel Viglione
    Daniel Viglione about 8 years
    The second option looks really good. Have to remember that one.