4

For a Rails 3.1 app, some of my site wide JavaScript is only included when certain real time, instance specific conditions are met. This means I can't put it in the new asset pipeline's application.js because that isn't parsed by erb for embedded Ruby within the current context. Basically, I'm including keyboard shortcuts, based on the current_user that is logged in.

My question: where should this JavaScript with embedded code go, so that the embedded Ruby is still parsed for each page access with the proper context (i.e. current, logged in user)?

The answer seems to just be to put it in the application.html.erb layout view at the bottom, but this seams like I'm hiding away javascript code in a non intuitive location.

I've tried creating an application2.js.erb file, but then I got errors about undefined variables, which I think might be because the asset engine only parses this file once before the output is cached and the scope isn't correct yet for things like current_user.

So, using application.html.erb works just fine here, and this isn't so much a question of how to get it to work functionally. Instead, I'm wondering if there's a more elegant way to incorporate the asset pipeline model here with my requirements and still keep most of my JavaScript in the assets/javascripts directory.

4
  • 1
    You don't ever need to mess Ruby code with JavaScript – fl00r Sep 5 '11 at 22:33
  • 2
    @fl00r: Not sure how you mean that. My very concrete example shows why there's reasons to mix Ruby into your JavaScript (i.e. only certain users should get JavaScript blocks included). Similarly, my JavaScript could include dynamic bits, like the current user's name e.g. – Joost Schuur Sep 5 '11 at 22:38
  • You always can do just the same job with unobtrusive js – fl00r Sep 6 '11 at 0:00
  • fl00r: That doesn't really apply here. I'm server-side excluding certain things, based on the permission level of the user. If you have specific code examples you'd like to suggest to provide some more details, I'd love to see that in a separate answer here. – Joost Schuur Sep 7 '11 at 6:11
0

You should try to create app/assets/javascripts/application2.js.erb (or whatever better name you come up with)

And then put something like this in your app/assets/javascripts/application.js:

//= require application2

And then you can have

<%= javascript_include_tag 'application2' %>

wherever you want - for example in your application.html.erb.

Btw, if you want to customize what's included on a per-view basis you might find content_for useful. Check out this screencast

3
  • There was already a 'require_tree .' directive by default, so the new JS file would have been included based on that. However, I think the asset pipeline only parses this template once and then caches the results. This means no page specific context like the current_user object, and that will error out. Note BTW, that application.js and application.js.erb don't coexist nicely (the latter gets ignored), so I had to pick a slightly different name. – Joost Schuur Sep 5 '11 at 22:42
  • Great point about content_for, and I've actually used it for JS in a previous app before 3.1. However, in this case, it's not controller specific JS, so it's best just kept in the global application layout. That works, but I'm mainly asking if this particular example means I can't elegantly have all my JS reside in the assets folder after all. – Joost Schuur Sep 5 '11 at 22:45
  • Hm. I see. I also use this technique: railscasts.com/episodes/205-unobtrusive-javascript. It works by setting some custom attribute in .html.erb and than reading it with jquery. no js.erb needed then. But I might be missing something obvious for your case :) I'm myself is still learning :) – dimsuz Sep 5 '11 at 22:52
0

Ok, about unobtrusive js. It will be just a cocept (HAML):

In your view somewhere

# hotkeys are "Ctrl+C", "Ctrl+A"
-current_user.hotkeys.each do |hotkey|
  %hotkey{ "data-key" => hotkey.key, "data-behavior" => hotkey.fn  }

Then in your application.js

$(document).ready(function(){
  if($("hotkey").length > 0){
    $("hotkey").each{function(this){
      key = $(this).data("key");
      fn  = $(this).data("behavior");
      $(document).bind('keydown', key, fn);
    }}
  }
})

So just the same JS will extract from HTML hotkeys data and then bind it.

1
  • Honestly, this just overcomplicates things, the way you're creating hotkeys attributes on the current_user e.g.. In the end, you're still adding JavaScript to the view (the fn code snippet called). My question is also less specifically about the hotkey binding in my speific example. – Joost Schuur Sep 7 '11 at 15:39
0

As some people have pointed out, the two options are:

  • Put your javascript inside the view (and as you say, this doesn't feel quite right).
  • Put it in a javascript file. Make a conditional inside your view that includes this javascript file if certain conditions are met.

If you need to pass more instance variables from the controller to your javascript, this gem called gon can make your life easier.

This allows you to use the default asset pipeline using the following javascript:

if(gon.conditional){
  //your embedded js code here
}

If you want to know more about this gem, checkout this railcast where everything gets explained.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.