ruby-on-rails angularjs turbolinks

ruby on rails - Usando angularjs con turbolinks



ruby-on-rails (9)

Estoy tratando de usar el marco de Angularjs en mi aplicación con turbolinks. Después de cambiar la página, no inicialice los nuevos listers de eventos. ¿Es alguna forma de hacerlo funcionar? ¡Gracias por adelantado!


Turbolinks obtiene automáticamente la página, intercambia su <body> y fusiona su <head> , todo sin incurrir en el costo de una carga de página completa.

https://github.com/turbolinks/turbolinks#turbolinks

Entonces, en lugar de agregar la directiva ng-app en el elemento <html> , podemos hacerlo en el elemento <body> .

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


AngularJS vs. Turbolinks

Tanto Turbolinks como AnguluarJS se pueden usar para hacer que una aplicación web responda más rápido, en el sentido de que, en respuesta a una interacción del usuario, algo sucede en la página web sin recargar y volver a enviar toda la página.

Se diferencian en el siguiente aspecto:

  • AngularJS lo ayuda a crear una aplicación de cliente enriquecida , donde escribe una gran cantidad de código JavaScript que se ejecuta en la máquina cliente. Este código hace que el sitio sea interactivo para el usuario. Se comunica con el backend del lado del servidor, es decir, con la aplicación Rails, utilizando una API JSON.

  • Turbolinks , por otro lado, ayuda a hacer que el sitio sea interactivo sin requerir que usted codifique JavaScript. Le permite mantener el código de Ruby / Rails ejecutado en el lado del servidor y aún así, "mágicamente", usar AJAX para reemplazar, y por lo tanto redirigir, solo las partes de la página que han cambiado.

Donde Turbolinks es fuerte al permitirle usar este poderoso mecanismo AJAX sin hacer nada a mano y solo codifique Ruby / Rails, podría surgir una etapa a medida que su aplicación crezca, donde le gustaría integrar un marco de JavaScript como AngularJS.

Especialmente en esta etapa intermedia, donde le gustaría integrar sucesivamente AngularJS en su aplicación, un componente a la vez, puede ser perfectamente lógico ejecutar Angular JS y Turbolinks juntos.

Cómo usar AngularJS y Turbolinks juntos

Utilice la devolución de llamada para arrancar manualmente Angular

En su código Angular, tiene una línea que define su módulo de aplicación, algo como esto:

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

Este código se ejecuta cuando se carga la página. Pero como Turbolinks simplemente reemplaza una parte de la página y evita una carga completa de la página, debe asegurarse de que la aplicación angular esté correctamente inicializada ("bootstrapped"), incluso después de dichas recargas parciales realizadas por Turbolinks. Por lo tanto, reemplace la declaración del módulo anterior por el siguiente código:

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

No arrancar automáticamente

Con frecuencia, en los tutoriales se ve cómo arrancar automáticamente una aplicación Angular utilizando el atributo ng-app en su código HTML.

<!-- app/views/layouts/my_layout.html.erb --> <!-- without turbolinks --> <html ng-app="YourApplication"> ...

Pero el uso de este mecanismo junto con el programa de arranque manual mostrado anteriormente causaría que la aplicación se iniciara dos veces y, por lo tanto, frenaría la aplicación.

Por lo tanto, simplemente elimine este atributo ng-app :

<!-- app/views/layouts/my_layout.html.erb --> <!-- with turbolinks --> <html> ...

Otras lecturas


Usando Turbolinks y AngularJS juntos

+1 a @fiedl por una gran respuesta. Pero mi preferencia es hacer uso de page:change en concert with page:load porque ofrece cierta flexibilidad: el DOM puede recibir un evento page:load desde fuentes que no sean turbolinks, por lo que es posible que no desee que se active el mismo retorno de llamada.

Buscar una page:change , luego una page:load debería restringir su comportamiento de devolución de llamada únicamente a eventos instigados por 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();

(Esto permitirá / requerirá que mantenga su declaración ng-app en su html, como de costumbre cuando trabaja con AngularJS).


Agregue el siguiente controlador de eventos a su aplicación.

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);

Esto hará que la aplicación angular se inicie después de cada página cargada por Turbolinks.

Crédito a https://gist.github.com/ayamomiji/4736614


El complemento jquery.turbolinks puede desencadenar el arranque de módulos a través de directivas ng-app. Si está intentando arrancar manualmente sus módulos, jquery.turbolinks puede llevar a errores ng: btstrpd. Una advertencia que he encontrado es que jquery.turbolinks se basa en la page:load evento de page:load , que puede activarse antes de que cualquier nueva etiqueta <script> específica de la página termine de ejecutarse. Esto puede llevar a errores $injector:nomod si incluye definiciones de módulos fuera de application.js. Si realmente desea que sus módulos se definan en archivos javascript separados que solo se incluyen en ciertas páginas, simplemente puede deshabilitar los turbolinks en cualquier enlace a esas páginas específicas a través de data-no-turbolink .


En caso de error

Error no detectado: [ng: btstrpd] La aplicación ya se inició con este elemento ''documento''

después de actualizar a angular 1.2.x puede usarlo a continuación para solucionar el problema.

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); }); });

En la publicación anterior, @nates propuso cambiar angular.bootstrap(document, [''YourApplication'']) a angular.bootstrap("body", [''YourApplication'']) pero esto causa un destello de contenido no compilado.


Los Turbolinks intentan optimizar la representación de las páginas y podrían entrar en conflicto con el arranque normal de AngularJS.

Si está utilizando Turbolinks en algunos lugares de su aplicación y algunas partes, utilice Angular. Propongo esta solución elegante:

Cada enlace a una página que sea angularapp (donde usas ng-app = "appname") debe tener este atributo:

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

El segundo, mencionado en , está recargando / arrancando explícitamente cada aplicación ng al manejar la página: evento de carga. Me gustaría que sea intrusivo, sin mencionar que potencialmente estás cargando algo que no está en una página, por lo tanto, desperdiciando recursos.

Personalmente he usado la solución anterior.

Espero eso ayude


Según los comentarios que he visto, el único escenario válido para usar ambos juntos de una manera en que Angular entraría en conflicto con Turbolinks (por ejemplo, cuando permito que Angular maneje parte del enrutamiento) es si tengo una aplicación existente. Estoy tratando de portar a Angular.

Personalmente, si tuviera que hacer esto desde cero, creo que la mejor solución sería decidir qué debería manejar el enrutamiento y atenerse a eso. Si es Angular, entonces deshacerse de Turbolinks -> no hará mucho por ti si tienes algo cerca de una aplicación de una sola página. Si permite que Rails maneje el enrutamiento, entonces use Angular para organizar el comportamiento del lado del cliente que el servidor no puede procesar cuando entrega las plantillas.

¿Me estoy perdiendo un escenario, aquí? No me parece elegante tratar de dividir las responsabilidades de enrutamiento entre diferentes marcos, incluso en una aplicación grande ... ¿Hay algún otro escenario en el que Turbolinks interfiera con Angular además de actualizar la página o navegar a una nueva ruta?


Turbolinks no tiene mucho sentido con un marco MVC del lado del cliente. Turbolinks está acostumbrado a eliminar todo, excepto el cuerpo, de la respuesta del servidor. Con MVC del lado del cliente, debe pasar JSON al cliente, no HTML.

En cualquier caso, turbolinks crea sus propios callbacks.

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