Rails/[removed] How to inject rails variables into

2020-03-24 05:37发布

问题:

I want to write up a very simple javascript calculator in rails which multiplies the quantity of an input field by a number stored in a rails variable (@item.base_price)

So, on the javascript/coffeescript side of things, it's crudely this:

# app/assets/javascript/items.js.coffee
$ -> 
  $('#item_quantity').change ->
    quantity_val = $(this).val()
    $('#total_amount').html(quantity_val * <%= I_WANT_@ITEM.BASE_PRICE_HERE %>)

I'm aware of how I can do this via an ajax call on each change() call, but I figure there has to be an elegant, hopefully unobtrusive rails way which doesn't hit the server each time.

Any suggestions very appreciated

回答1:

If you are using rails 3.1 you can take advantage of the assets pipeline to do some pre-processing on the javascript files before you serve them up. To do this just change the file extension from:

items.js.coffee

to

items.js.coffee.erb

then you can add ruby to your javascript just like in your view with <%= %> tags. The only gotcha you might run into, is that your items.js file will be served to every request to any of your app's controller methods. So its best to write a helper method that will return the value only if the instance variable is initialized

For example in items_helper.rb

def item_price
    if @item
        @item.base_price
    else
        0
    end
end

EDIT: more about assets pipeline here:

http://guides.rubyonrails.org/asset_pipeline.html



回答2:

Although... if you serve up the Javascript files as static assets, this might not be optimal. I typically put a script tag in the head section of the HTML with the variable. That way, the JS doesn't have to be rebuild and the browser cache for it invalidated. E.g.:

<head>
  <script type="text/javascript">
    var myGlobalVariable = <%= @global_js_variable %>;
  </script>
</head>

While this stinks for keeping things in their separate namespaces, it does reduce the overhead of shipping new Javascript files to the client.

Just a thought.



回答3:

Using the asset pipeline to process your CoffeeScript files with ERB on a per-request basis may be fine for development, but it will be a bottleneck in production.

In production, I use a global variable or a specific property of a global variable to reduce pollution.

At the bottom of the page's ERB view:

<script>
  //<![CDATA[
    window.MyApp = window.MyApp || {};
    window.MyApp.itemBasePrice = <%=j @item.base_price.to_json.html_safe %>;
  //]]>
</script>

Always put scripts at the bottom (and stylesheets at the top) of your page since it leads to faster perceived page load times.

I highly recommend reading this article on How to securely bootstrap JSON in a Rails view. The latest version of Rails at the time of this writing is vulnerable to XSS attacks when bootstrapping JSON in this way. If you just have a simple number, it may not be an issue. But I find that after people see your code working, they tend to simply copy/paste it to more complicated situations without thinking about the consequences.

Alternatively, if your data has a natural container, you can embed it in a data attribute.

<div id="item" data-base-price="<%=j @item.base_price.to_json.html_safe %>"></div>

Accessing it in your CoffeeScript:

console.log $('#item').data('basePrice')