Ruby on Rails 7:如何从控制器调用JavaScript函数并传递变量
Rails 7 控制器变量传递与JS执行顺序问题解决
问题说明
我在app/javascript/scripts/script_scripts.js路径下有一个JS文件,文件关联正常。目前id="create"的元素功能正常,id="func"的事件监听器也能正常工作,但存在两个问题:
- JS代码会在控制器调用前执行,导致
id="funner"的事件监听器无法获取控制器中定义的@txt变量; - 不清楚在Rails 7中如何正确使用
onclick执行函数。
需求:要么实现控制器先设置变量再调用JS函数,要么实现从控制器直接调用该JS文件。
现有代码
1. JS文件(app/javascript/scripts/script_scripts.js)
function fun() { alerts('<%= @txt %>') }; function funs(txt) { alerts(txt) }; document.getElementById('funner').addEventListener('click', function() { alert("<%= @txt %>") }); document.getElementById('func').addEventListener('click', function() { alert("TADA") }); var test = document.getElementById('create').innerHTML = "Hello World! In script.js Test";
2. ERB视图文件
<% content_for :head do %> <%= javascript_import_module_tag "script" %> <% end %> <%= button_to "Click Me", home_test_path, onclick:"fun()" %> <%= button_to "Click Me", home_test_path, onclick:"funs(#{@txt})" %> <%= button_to "Click Me", home_test_path, id:"funner" %> <%= button_to "Click Me", home_test_path, id:"func" %> <div id="create"></div>
3. Home控制器test方法
class HomeController < ApplicationController def test @txt = "YAY" end end
(注:原代码中test方法定义缺少def,已修正)
解决方案
方案1:修正JS执行时机,正确传递控制器变量
单独的JS文件无法解析ERB的<%= %>语法,且Rails 7默认使用Turbo,需确保JS在页面完全加载后执行,同时通过视图传递变量。
步骤1:修改ERB视图
- 用普通
<button>替代button_to避免默认表单提交(或在onclick中阻止默认行为); - 将
@txt通过全局变量或data属性传递给JS:
<% content_for :head do %> <%= javascript_import_module_tag "script" %> <!-- 定义全局变量传递控制器值 --> <script> window.controllerTxt = '<%= j @txt %>'; </script> <% end %> <!-- 普通button执行前端函数,阻止默认提交 --> <button onclick="fun(); return false;">Click Me</button> <button onclick="funs('<%= j @txt %>'); return false;">Click Me</button> <!-- 用data属性传递变量给funner按钮 --> <%= button_to "Click Me", home_test_path, id:"funner", data: { txt: @txt } %> <%= button_to "Click Me", home_test_path, id:"func" %> <div id="create"></div>
步骤2:修改JS文件
使用Turbo的turbo:load事件确保页面就绪后执行代码,从全局变量或data属性读取值:
// 等待Turbo完成页面加载 document.addEventListener('turbo:load', function() { // 初始化create元素 document.getElementById('create').innerHTML = "Hello World! In script.js Test"; // 绑定func按钮事件 const funcBtn = document.getElementById('func'); if (funcBtn) { funcBtn.addEventListener('click', function() { alert("TADA"); }); } // 绑定funner按钮事件,从data属性获取变量 const funnerBtn = document.getElementById('funner'); if (funnerBtn) { funnerBtn.addEventListener('click', function(e) { // 阻止默认表单提交 e.preventDefault(); alert(this.dataset.txt); }); } }); // 全局声明函数,供onclick调用 window.fun = function() { alert(window.controllerTxt); }; window.funs = function(txt) { alert(txt); };
方案2:从控制器直接调用JS
在控制器中渲染JS代码,确保控制器先设置变量再执行逻辑:
修改Home控制器test方法
class HomeController < ApplicationController def test @txt = "YAY" # 直接渲染JS代码,执行需要的逻辑 render js: "alert('#{j @txt}'); document.getElementById('create').innerHTML = 'Hello World! In script.js Test';" # 也可以调用JS文件中的函数 # render js: "fun('#{j @txt}');" end end
这种方式下,点击button_to会触发控制器方法,返回的JS代码会直接在前端执行,保证变量已被设置。
内容的提问来源于stack exchange,提问作者Ese10
相关产品推荐
相关产品推荐

