在特定页面执行JavaScript的正确“Rails”方式

10

我试图在特定页面上运行javascript,我的唯一解决方案似乎是一个反模式。我在 assets/javascripts/ 内生成了 controller.js 。我正在使用 gem 'jquery-turbolinks' ,并且我有以下类似的代码:

$(document).ready(function () {
    //Initiate DataTables ect..
    }
)

这段代码在每个页面上都触发,所以我在其中添加了以下内容。

if ($('#page-specific_element').length > 0) {
    //Initiate Datatables ect.
}

我的问题是,是否有一种方法可以设置Rails仅使用特定控制器所需的JavaScript文件,或者逻辑门控制元素搜索是最佳解决方案?


我曾尝试将 <%= javascript_include_tag params[:controller] %> 添加到 application.html.erb 文件中,并在解决方案中删除 application.js 中的 //= require_tree . ,但是我发现需要将每个“ controller.js” 添加到预编译资源文件中,这似乎走错了方向。 - Maleki
2个回答

17
这是一个相当不错的TurboLinks特定解决方案:
<body data-controller="<%= controller.controller_name %>" data-action="<%= controller.action_name %>">

// Instead of listening for document ready
$(document).on('users#show:loaded', function(){
    $('#logger').append('<li>Users Show action loaded</li>');
});

// Instead of listening for document ready
$(document).on('users:loaded', function(){
    $('#logger').append('<li>Some users controller method loaded</li>');
});

// Turbolinks fires "page:change" when it changes the page content.
// you could change this to just document ready if you are not using 
// turbolinks.
$(document).on("page:change", function(){
  var data = $('body').data();
  $(document).trigger(data.controller + ':loaded');
  $(document).trigger(data.controller + '#' + data.action + ':loaded');
});

// This is just for demonstration purposes...
$(document).trigger("page:change");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body data-controller="users" data-action="show">

  <ul id="logger"></ul>
</body>

请注意,如果您正在使用Turbolinks,那么您将拥有一个长时间运行的、维护状态的持久会话。这意味着,如果您在视图中添加内联脚本标签或加载其他JavaScript文件,它们将停留在浏览器的内存中。

因此,您可能希望在page:before-unload上清理您创建的任何页面特定的事件处理程序。


极其优雅的未被发现的Turbolinks宝石! - user3670743
我已经基于这个模式发布了一个宝石。https://github.com/maxcal/pagescript - max
@max,无法在Rails 5.1.3中安装此gem。 - Syamsoul Azrien
@SyamsoulAzrien,你能在Github上创建一个问题吗? - max

1

有很多方法可以做到这一点。我将描述其中一种。

我使用this old gem来将所有我的.module.js文件包装在commonjs模块中。

然后我有一个帮助程序,会将类似以下内容写入每个HTML页面头部的<script>标签中。

<script>
  var App {
    pageModule: 'posts/create'
  };
</script>

posts/create 是从当前控制器和动作呈现请求的简化混合生成的。

然后我在我的 application.js 的 domready 中有类似以下内容的代码。

try { App.pageModule = require(App.pageModule); } catch(e) { console.log('%c' + e, 'color:red; background-color:yellow'); }

if (_.isPlainObject(App.pageModule) && _.has(App.pageModule, 'initialize')) {
  App.pageModule.initialize();
}

最后,我将拥有这样一个文件,它专门针对 /posts/create URL。

app/assets/posts/create.module.js

module.exports = {

  initialize: function() {
    // data-tables stuff here...
  }

}

谢谢您提供的解决方案。我在stackoverflow上找到了一个类似的问题https://dev59.com/0nA75IYBdhLWcg3wKluM?rq=1,但那里没有得到解决。 - Maleki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接