What is the scope of Ruby code in a js.erb file an

2019-06-14 10:26发布

问题:

I have a js.erb file in which I want to use asset_path. It is included in a view like this:

<%= javascript_include_tag "resources/gallery_resource" %>

This works fine while the asset path is self contained, however I need to use a variable defined in the controller (@resource) inside the asset path:

element.image = "<%= asset_path 'resources/galleries/'+@resource.uid+'/'+index+'.jpg' %>"

The problem being @resource is not defined. So what is the scope of this Ruby code if it doesn't have access to the controller? And how should I make the variable available here?

I understand how to make the variable accessible to the Javascript, but I want to make it available to the embedded Ruby.

回答1:

You will need to render it as a partial, and pass the resource in as a local:

<script>
  <%= render :partial => 'resources/gallery_resource', :locals => { :resource => @resource } %>
</script>

Also, in the gallery_resource.js.erb file you'll need to change @resource to just resource:

element.image = "<%= asset_path 'resources/galleries/'+resource.uid+'/'+index+'.jpg' %>"

If this is rendered in any other actions, they will also need to pass a :resource value in the :locals hash.

The scope of the js.erb files depends on a couple of things:

  • If the controller, itself, is rendering the file, then it has access to instance variables (like '@resource'). This would be the case with views/viewtype/show.js.erb, if it existed and if the browser requested a js response type. Just like with a .html.erb file.
  • If it's being rendered as a partial, then the only scope is what you pass in with the :locals hash. These are not instance variables, but local variables, so they're referenced as such (like 'resource').
  • Using javascript_include_tag, on the other hand, makes a tag in the HTML that refers to the JS file directly. This is alright if you really just need to include some javascript that isn't variable. These would either have to be somewhere under your app/assets/ directory or your public/ directory.

Files in app/assets or in public/ are not able to access variables in individual requests - they either don't get compiled (public/) or else they're pre-compiled only once (app/assets).

Presumably, the file in question is currently in your app/assets directory. In order to render it as a partial, move it somewhere beneath the views/ directory, put an underscore (_) in front of the name:

app/views/resources/_gallery_resource.js.erb


回答2:

For the record, my solution to this problem was to grab a base path using a token:

var base_path = "<%= asset_path 'resources/galleries/{{X}}.jpg' %>";

Then replace the token using a controller variable made avilable using the excellent gon gem

var imagePathFragment = gon.resource.uid+'/'+(index+1);
element.image = base_path.replace('{{X}}', imagePathFragment);