使用angularjs与turbolinks

我正在尝试在我的应用程序中使用Angularjs框架与turbolinks。 页面更改后,它不会初始化新的eventlisteners。 有什么办法让它工作? 提前致谢!

AngularJS与Turbolinks

Turbolinks以及AnguluarJS都可以用来使Web应用程序响应得更快,这是因为响应于用户交互,网页上发生的事情没有重新加载和重新渲染整个页面。

它们在以下方面有所不同:

  • AngularJS可以帮助您构build一个丰富的客户端应用程序 ,您可以在其中编写运行在客户端计算机上的大量JavaScript代码。 该代码使网站与用户交互。 它与服务器端后端进行通信,即使用Rails应用程序,使用JSON API。

  • 另一方面, Turbolinks有助于使网站互动,而无需编写JavaScript代码。 它允许您坚持在服务器端运行的Ruby / Rails代码 ,并且“神奇地”使用AJAX来replace并重新渲染仅改变页面的部分。

Turbolinks可以让你使用这个function强大的AJAX机制,而不需要手动做任何事情,只需要Ruby / Rails代码就可以了,随着应用程序的增长,你可能会想要集成一个JavaScript框架,比如AngularJS。

特别是在这个中间阶段,您希望将AngularJS集成到您的应用程序中,一次一个组件,一起运行Angular JS和Turbolinks可以非常有意义。

如何一起使用AngularJS和Turbolinks

使用callback来手动引导Angular

在您的Angular代码中,您有一行定义您的应用程序模块,如下所示:

# app/assets/javascripts/angular-app.js.coffee # without turbolinks @app = angular.module 'MyApplication', ['ngResource'] 

该代码在页面加载时运行。 但是由于Turbolinks只是replace了页面的一部分,并且阻止了整个页面加载,所以即使在Turbolinks完成了部分重新加载之后,也必须确保angular度应用程序已正确初始化(“引导”)。 因此,用下面的代码replace上面的模块声明:

 # app/assets/javascripts/angular-app.js.coffee # with turbolinks @app = angular.module 'MyApplication', ['ngResource'] $(document).on 'turbolinks:load', -> angular.bootstrap document.body, ['MyApplication'] 

不要自动引导

您经常在教程中看到如何使用HTML代码中的ng-app属性自动引导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
  • Turbolink上的Railscasts(解释callback): http : //railscasts.com/episodes/390-turbolinks
  • 演示应用程序: https : //github.com/fiedl/rails-5-angular-and-turbolinks-demo

Turbolinks尝试优化页面的呈现,并与AngularJS的正常引导相冲突。

如果您在应用程序的某些地方使用Turbolink,并且某些部件使用Angular。 我提出这个优雅的解决scheme

链接到angularapp(使用ng-app =“appname”的页面)的每个链接应具有以下属性:

 <a href="/myapp" data-no-turbolink>Say NO to Turbolinks</a>. 

第二个在Stackoverflow上提到的是通过处理page:load事件显式重载/引导每个ng-app。 我觉得这是侵入性的,更不用说你有可能加载不在页面上的东西,从而浪费资源。

我亲自使用了上述解决scheme。

希望能帮助到你

在错误的情况下

未捕获的错误:[ng:btstrpd]应用程序已经使用此元素“文档”引导

升级到angular1.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); }); }); 

在以前的post@natesbuild议更改angular.bootstrap(document, ['YourApplication'])angular.bootstrap("body", ['YourApplication'])但这会导致一个未编译内容的闪光。

Turbolinks与客户端MVC框架不太合理。 Turbolinks被用来从服务器响应中除去所有的身体。 使用客户端MVC,您应该将JSON传递给客户端,而不是HTML。

无论如何,turbolinks创build自己的callback。

 page:load page:fetch page:restore page:change 

jquery.turbolinks插件可以通过ng-app指令触发模块的引导。 如果您尝试手动引导您的模块,jquery.turbolinks可能会导致ng:btstrpd错误。 我发现的一个警告是,jquery.turbolinks依赖于page:load事件,这可以触发任何新的特定于页面的<script>标签完成运行。 如果在application.js之外包含模块定义,这可能会导致$injector:nomod错误。 如果您确实希望将模块定义在仅包含在特定页面上的单独的JavaScript文件中,则可以通过data-no-turbolink 禁用到这些特定页面的任何链接上data-no-turbolink

将以下事件处理程序添加到您的应用程序

CoffeeScript的:

 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度应用程序。

感谢https://gist.github.com/ayamomiji/4736614

根据我所看到的评论,以Angular与Turbolinks冲突的方式(例如允许Angular处理某些路由)一起使用的唯一有效scheme是如果我有一个现有的应用程序,试图移植到Angular。

就个人而言,如果我从头开始这样做,我认为最好的解决办法是决定什么应该处理路由并坚持。 如果是Angular,比摆脱Turbolinks – >它不会为你做很多,如果你有一些接近单页面的应用程序。 如果您允许Rails处理路由,那么只需使用Angular来组织服务器在提供模板时无法处理的客户端行为。

我错过了一个场景吗? 对我来说,尝试在不同框架之间分配路由责任似乎并不高雅,即使是在大型应用程序中……除了刷新页面或导航到新路线之外,Turbolink还会干扰Angular的其他情况吗?

一起使用Turbolinks和AngularJS

+1给@fiedl一个很好的答案。 但我的首选是使用page:changepage:load一起page:change ,因为这提供了一些灵活性:DOM可以接收page:load从turbolinks以外的来源page:load事件,所以你可能不希望有相同的callback火。

看着一个page:change然后一个page:load应该限制​​你的callback行为,以单独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

所以,不要在<html>元素上附加ng-app指令,而是在<body>元素上执行。

 <html> <body ng-app=“yourApplicationModuleName"> </body> </html>