我正在尝试在我的应用程序中使用 Angularjs 框架和 turbolinks。页面更改后,它不会初始化新的事件监听器。有什么办法让它工作吗?提前致谢!
9 回答
AngularJS 与 Turbolinks
Turbolinks和AnguluarJS都可以用来使 Web 应用程序响应更快,因为响应用户交互,网页上会发生一些事情,而无需重新加载和重新渲染整个页面。
它们在以下方面有所不同:
AngularJS帮助您构建一个富客户端应用程序,您可以在其中编写大量运行在客户端机器上的 JavaScript 代码。此代码使站点与用户交互。它使用 JSON API 与服务器端后端(即 Rails 应用程序)进行通信。
另一方面,Turbolinks有助于使网站具有交互性,而无需您编写 JavaScript 代码。它允许您坚持在服务器端运行的 Ruby/Rails 代码,并且仍然“神奇地”使用 AJAX 替换并因此重新呈现页面中已更改的部分。
Turbolinks 的强大之处在于允许您使用这种强大的 AJAX 机制,而无需手动执行任何操作,只需编写 Ruby/Rails 代码,随着应用程序的增长,您可能会想要集成 JavaScript 框架,例如 AngularJS。
尤其是在这个中间阶段,您希望将 AngularJS 依次集成到您的应用程序中,一次一个组件,将 Angular JS 和 Turbolinks 一起运行是非常有意义的。
如何一起使用 AngularJS 和 Turbolinks
使用回调手动引导 Angular
在您的 Angular 代码中,您有一行定义您的应用程序模块,如下所示:
# app/assets/javascripts/angular-app.js.coffee
# without turbolinks
@app = angular.module 'MyApplication', ['ngResource']
此代码在页面加载时运行。但是由于 Turbolinks 只是替换了页面的一部分并阻止了整个页面的加载,因此您必须确保 angular 应用程序已正确初始化(“引导”),即使在 Turbolinks 完成此类部分重新加载之后也是如此。因此,将上面的模块声明替换为以下代码:
# app/assets/javascripts/angular-app.js.coffee
# with turbolinks
@app = angular.module 'MyApplication', ['ngResource']
$(document).on 'turbolinks:load', ->
angular.bootstrap document.body, ['MyApplication']
不要自动引导
您经常在教程中看到如何使用ng-app
HTML 代码中的属性自动引导 Angular 应用程序。
<!-- app/views/layouts/my_layout.html.erb -->
<!-- without turbolinks -->
<html ng-app="YourApplication">
...
但是将这种机制与上面显示的手动引导一起使用会导致应用程序引导两次,因此会阻止应用程序。
因此,只需删除此ng-app
属性:
<!-- app/views/layouts/my_layout.html.erb -->
<!-- with turbolinks -->
<html>
...
延伸阅读
- AngularJS 引导:http ://docs.angularjs.org/guide/bootstrap
- Turbolinks 上的 Railscasts(解释回调):http ://railscasts.com/episodes/390-turbolinks
- 演示应用程序:https ://github.com/fiedl/rails-5-angular-and-turbolinks-demo
Turbolinks 试图优化页面的渲染并且会与 AngularJS 的正常引导冲突。
如果您在应用程序的某些地方使用 Turbolinks,并且某些部分使用 Angular。我提出了这个优雅的解决方案:
每个指向 angularapp 页面的链接(使用 ng-app="appname")都应该具有以下属性:
<a href="/myapp" data-no-turbolink>Say NO to Turbolinks</a>.
第二个 - 在 Stackoverflow 上提到的是通过处理 page:load 事件显式地重新加载/引导每个 ng-app。我认为这是侵入性的,更不用说您可能会加载页面上没有的内容,从而浪费资源。
我个人使用过上述解决方案。
希望能帮助到你
如果出现错误
未捕获的错误:[ng:btstrpd] 应用程序已使用此元素“文档”引导
升级到 angular 1.2.x 后,您可以使用下面的方法来解决问题。
angular.module('App').run(function($compile, $rootScope, $document) {
return $document.on('page:load', function() {
var body, compiled;
body = angular.element('body');
compiled = $compile(body.html())($rootScope);
return body.html(compiled);
});
});
在之前的帖子中,@nates 建议更改angular.bootstrap(document, ['YourApplication'])
为angular.bootstrap("body", ['YourApplication'])
,但这会导致未编译内容的闪现。
将以下事件处理程序添加到您的应用程序。
咖啡脚本:
bootstrapAngular = ->
$('[ng-app]').each ->
module = $(this).attr('ng-app')
angular.bootstrap(this, [module])
$(document).on('page:load', bootstrapAngular)
Javascript:
function bootstrapAngular() {
$('[ng-app]').each(function() {
var module = $(this).attr('ng-app');
angular.bootstrap(this, [module]);
});
};
$(document).on('page:load', bootstrapAngular);
这将导致在 Turbolinks 加载每个页面后启动 Angular 应用程序。
Turbolinks 对于客户端 MVC 框架不太有意义。Turbolinks 用于从服务器响应中去除除正文之外的所有内容。使用客户端 MVC,您应该只将 JSON 传递给客户端,而不是 HTML。
无论如何,turbolinks 都会创建自己的回调。
page:load
page:fetch
page:restore
page:change
jquery.turbolinks插件可以通过 ng-app 指令触发模块的引导。如果您尝试手动引导模块,jquery.turbolinks 可能会导致 ng:btstrpd 错误。我发现的一个警告是 jquery.turbolinks 依赖于该page:load
事件,该事件可以在任何新的特定于页面的<script>
标签完成运行之前触发。$injector:nomod
如果您在 application.js 之外包含模块定义,这可能会导致错误。如果您真的希望在仅包含在某些页面上的单独 javascript 文件中定义您的模块,您可以通过data-no-turbolink
.
根据我看到的评论,在 Angular 与 Turbolinks 冲突的情况下(例如我允许 Angular 处理某些路由)同时使用两者的唯一有效场景是,如果我有一个现有的应用程序,我我试图移植到 Angular。
就个人而言,如果我要从头开始这样做,我认为最好的解决方案是决定应该处理路由并坚持下去。如果是 Angular,那么摆脱 Turbolinks -> 如果您有接近单页应用程序的东西,它不会为您做太多事情。如果您允许 Rails 处理路由,那么只需使用 Angular 来组织在提供模板时服务器无法处理的客户端行为。
我在这里错过了一个场景吗?即使在大型应用程序中,尝试在不同框架之间拆分路由职责对我来说似乎并不优雅......除了刷新页面或导航到新路由之外,是否存在其他一些 Turbolinks 会干扰 Angular 的场景?
一起使用 Turbolinks 和 AngularJS
向@fiedl +1 以获得很好的答案。但我更喜欢使用page:change
与,page:load
因为这提供了一些灵活性:DOM 可以page:load
从 turbolinks 以外的源接收事件,所以你可能不希望有相同的回调触发。
监视 a page:change
,然后apage:load
应该将您的回调行为限制为仅由 turbolinks 发起的事件。
function boostrapAngularJS () {
angular.bootstrap(document.body, ['My Application']);
addCallbackToPageChange();
}
function addCallbackToPageChange() {
angular.element(document).one('page:change', function () {
angular.element(this).one('page:load', boostrapAngularJS);
});
}
addCallbackToPageChange();
(这将允许/要求您将ng-app
声明保留在 html 中,与使用 AngularJS 时一样。)
Turbolinks 自动获取页面、交换其
<body>
并合并其<head>
,所有这些都不会产生整个页面加载的成本。
https://github.com/turbolinks/turbolinks#turbolinks
因此,我们可以在元素上执行,而不是在元素上添加ng-app
指令。<html>
<body>
<html>
<body ng-app=“yourApplicationModuleName">
</body>
</html>