Rails/JQuery技术问题:如何在js.erb中传值及实现价格联动更新
其实js.erb是服务器端渲染的JavaScript模板,它本身是先在服务器执行Ruby代码,生成可执行的JS后再发给浏览器的。所以直接让jQuery“传数据”给js.erb里的Ruby不太对——正确的逻辑是:前端用jQuery发AJAX请求把数据传给后台控制器(Ruby编写),控制器处理完后,再通过js.erb模板把处理结果(或需要的变量)注入到JS中,最终在浏览器执行。
给你举个完整的例子:
前端发起AJAX请求(携带数据)
可以在视图里直接写这段JS,也可以放在单独的JS文件中:$('#send-data-btn').click(function() { // 准备要传给后台的数据 const userInput = { product_id: 10, quantity: 3 }; $.ajax({ url: '/products/update_quantity', // 对应后台的路由地址 method: 'POST', data: userInput, dataType: 'script' // 告诉服务器我们要接收JS格式的响应 }); });后台控制器接收并处理数据
在对应的控制器中编写处理逻辑:# app/controllers/products_controller.rb def update_quantity @product = Product.find(params[:product_id]) @new_quantity = params[:quantity].to_i # 这里可以添加数据库操作,比如更新产品库存 # @product.update(stock: @product.stock - @new_quantity) end用js.erb返回处理结果
创建对应的js.erb模板,里面可以直接使用控制器的@变量,生成浏览器能执行的JS代码:// app/views/products/update_quantity.js.erb // 用j(escape_javascript的简写)转义特殊字符,避免JS语法错误 alert("已处理产品<%= j @product.id %>,数量:<%= j @new_quantity %>"); // 也可以直接操作DOM更新页面内容 $('#product-stock').text("剩余库存:<%= j @product.stock %>");
核心要记住:js.erb是服务器先跑Ruby代码再输出JS,所以前端数据必须先通过AJAX传到后台,才能被Ruby获取到。
给你两种方案,你可以根据场景选择:
方案1:前端缓存数据(无需AJAX,响应更快)
如果产品定价数据不会实时变化,直接把所有价格提前渲染到前端的JS对象中,监听下拉框变化后直接取值更新span即可:
修改你的视图代码如下:
<label>Size</label> <select id="volumeinfo" name="volumeinfo"> <%= options_from_collection_for_select(@product.prices, :id, :volume_info) %> </select> <span id="price"></span> <script> // 把所有ProductPrice的id和价格对应关系存成JS对象 const priceLookup = { <% @product.prices.each do |price_item| %> <%= price_item.id %>: <%= price_item.price %>, <% end %> }; // 监听下拉框的change事件 $('#volumeinfo').on('change', function() { const selectedPriceId = $(this).val(); // 从缓存中取出对应价格,更新到span标签(可以按需添加价格格式,比如$符号) $('#price').text("$" + priceLookup[selectedPriceId]); }); // 页面加载完成时,默认显示第一个选项的价格 $(document).ready(function() { const defaultPriceId = $('#volumeinfo').val(); $('#price').text("$" + priceLookup[defaultPriceId]); }); </script>
方案2:AJAX请求后台获取实时价格
如果价格可能动态变化(比如后台会随时更新),就用AJAX每次选中后去后台获取最新价格:
视图中的JS代码
$('#volumeinfo').on('change', function() { const priceId = $(this).val(); $.ajax({ url: `/product_prices/${priceId}`, // 对应后台的路由地址 method: 'GET', dataType: 'json', success: function(response) { // 拿到后台返回的价格,更新span标签 $('#price').text("$" + response.price); }, error: function() { $('#price').text("获取价格失败"); } }); }); // 页面加载完成时,默认请求一次第一个选项的价格 $(document).ready(function() { const defaultPriceId = $('#volumeinfo').val(); $.ajax({ url: `/product_prices/${defaultPriceId}`, method: 'GET', dataType: 'json', success: function(response) { $('#price').text("$" + response.price); } }); });后台控制器与路由配置
首先在ProductPricesController中添加show动作,返回JSON格式的价格数据:# app/controllers/product_prices_controller.rb def show @product_price = ProductPrice.find(params[:id]) render json: { price: @product_price.price } end然后在
routes.rb中添加对应的路由:resources :product_prices, only: [:show]
两种方案各有优劣:方案1无需发送请求,响应速度快;方案2能获取到最新的价格数据,适合价格频繁变动的场景。
内容的提问来源于stack exchange,提问作者David S

