web-dev-qa-db-fra.com

Utiliser angularjs avec turbolinks

J'essaie d'utiliser le framework Angularjs dans mon application avec des turboliens. Après le changement de page, il n'initialise pas les nouveaux écouteurs d'événements. Est-ce un moyen de le faire fonctionner? Merci d'avance!

59
Pavel

AngularJS contre Turbolinks

Turbolinks ainsi que AnguluarJS peuvent tous deux être utilisés pour créer une application Web répondre plus rapidement, dans le sens où en réponse à une interaction utilisateur, quelque chose se passe sur la page Web sans recharger ni relancer la page entière.

Ils diffèrent sur les points suivants:

  • AngularJS vous aide à créer une application côté client riche , où vous écrire beaucoup de code JavaScript qui s'exécute sur la machine cliente. Ce code rend le site interactif pour l'utilisateur. Il communique avec le backend côté serveur, c'est-à-dire avec l'application Rails, à l'aide d'une API JSON.

  • Turbolinks , d'autre part, contribue à rendre le site interactif sans vous obliger à coder JavaScript. Il vous permet de coller au code Ruby/Rails exécuté côté serveur et toujours, "comme par magie", utiliser AJAX pour remplacer, et donc rendre à nouveau, uniquement les parties de la page qui ont changé.

Lorsque Turbolinks est fort pour vous permettre d'utiliser ce puissant mécanisme AJAX sans rien faire à la main et juste coder Ruby/Rails, il peut arriver une étape, à mesure que votre application se développe, où vous souhaitez intégrer un framework JavaScript tel que AngularJS.

Surtout dans cette étape intermédiaire, où vous souhaitez intégrer successivement AngularJS dans votre application, un composant à la fois, il peut être parfaitement logique d'exécuter Angular JS et Turbolinks ensemble.

Comment utiliser AngularJS et Turbolinks ensemble

Utilisez le rappel pour bootstrap Angular manuellement

Dans votre Angular, vous avez une ligne définissant votre module d'application, quelque chose comme ceci:

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

Ce code est exécuté lorsque la page est chargée. Mais comme Turbolinks ne fait que remplacer une partie de la page et empêche un chargement complet de la page, vous devez vous assurer que l'application angular est correctement initialisée ("bootstrapped"), même après ces rechargements partiels effectués par Turbolinks. Ainsi, remplacez la déclaration de module ci-dessus par le code suivant:

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

$(document).on 'turbolinks:load', ->
  angular.bootstrap document.body, ['MyApplication']

Ne pas bootstrap automatiquement

Vous voyez souvent dans les tutoriels comment bootstrap an Angular app automatiquement en utilisant le ng-app attribut dans votre code HTML.

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

Mais l'utilisation de ce mécanisme avec le manuel bootstrap illustré ci-dessus entraînerait l'application à bootstrap deux fois et, par conséquent, freinerait l'application).

Ainsi, supprimez simplement ce ng-app attribut:

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

Lectures complémentaires

125
fiedl

Les Turbolinks tentent d'optimiser le rendu des pages et entreraient en conflit avec le bootstraping normal d'AngularJS.

Si vous utilisez Turbolinks à certains endroits de votre application et que certaines parties utilisent Angular. Je propose cette élégante solution:

Chaque lien vers une page qui est angularapp (où vous utilisez ng-app = "appname") doit avoir cet attribut:

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

Le second - mentionné sur Stackoverflow est le rechargement/amorçage explicite de chaque ng-app en gérant l'événement page: load. Je dirais que c'est intrusif, sans mentionner que vous chargez potentiellement quelque chose qui n'est pas sur une page, ce qui gaspille des ressources.

J'ai personnellement utilisé la solution ci-dessus.

J'espère que cela aide

9
jeveloper

En cas de bug

Erreur non interceptée: [ng: btstrpd] Application déjà amorcée avec cet élément 'document'

après la mise à niveau vers angular 1.2.x, vous pouvez utiliser ci-dessous pour résoudre le problème.

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

Dans le post précédent, @nates proposait de changer angular.bootstrap(document, ['YourApplication']) en angular.bootstrap("body", ['YourApplication']) mais cela provoque un flash de contenu non compilé.

4
Artur Trzop

Le plugin jquery.turbolinks peut déclencher l'amorçage des modules via les directives ng-app. Si vous essayez manuellement bootstrap vos modules, jquery.turbolinks peut entraîner des erreurs ng: btstrpd. Une mise en garde que j'ai trouvée est que jquery.turbolinks repose sur le page:load événement, qui peut se déclencher avant toute nouvelle page spécifique <script> les tags finissent de fonctionner. Cela peut mener à $injector:nomod erreurs si vous incluez des définitions de module en dehors du fichier application.js. Si vous voulez vraiment que vos modules soient définis dans des fichiers javascript séparés qui ne sont inclus que sur certaines pages, vous pouvez simplement désactiver les turboliens sur tous les liens vers ces pages spécifiques via data-no-turbolink.

1

Ajoutez le gestionnaire d'événements suivant à votre application.

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

Cela entraînera le démarrage de l'application angular après chaque page chargée par Turbolinks.

Crédit à https://Gist.github.com/ayamomiji/4736614

1
Robin Daugherty

Turbolinks n'a pas vraiment de sens avec un framework MVC côté client. Turbolinks est utilisé pour supprimer tout sauf le corps de la réponse du serveur. Avec MVC côté client, vous devez simplement transmettre JSON au client, pas HTML.

Dans tous les cas, turbolinks crée ses propres rappels.

page:load
page:fetch
page:restore
page:change
1
cpuguy83

Utilisation simultanée de Turbolinks et AngularJS

+1 à @fiedl pour une excellente réponse. Mais ma préférence est d'utiliser page:change de concert avec page:load car cela offre une certaine flexibilité: le DOM peut recevoir un page:load événement provenant de sources autres que les turboliens, il est donc possible que vous ne souhaitiez pas avoir le même feu de rappel.

À la recherche d'un page:change, alors a page:load devrait limiter votre comportement de rappel aux seuls événements déclenchés par 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();

(Cela vous permettra/vous obligera à conserver votre ng-app déclaration dans votre html, comme d'habitude lorsque vous travaillez avec AngularJS.)

0
JellicleCat

Turbolinks récupère automatiquement la page, échange son <body>, et fusionne son <head>, le tout sans encourir le coût d'une page entière.

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

Donc, au lieu d'ajouter ng-app directive sur le <html> élément, nous pouvons simplement le faire sur le <body> élément.

<html>
  <body ng-app=“yourApplicationModuleName">
  </body>
</html>
0
Repolês

Sur la base des commentaires que j'ai vus, le seul scénario valide pour utiliser les deux ensemble d'une manière où Angular entrerait en conflit avec Turbolinks (comme où j'autorise Angular = pour gérer une partie du routage) est si j'ai une application existante que j'essaie de porter sur Angular.

Personnellement, si je devais le faire à partir de zéro, je pense que la meilleure solution serait de décider de ce qui devrait gérer le routage et de s'en tenir à cela. Si Angular, alors débarrassez-vous de Turbolinks -> cela ne fera pas grand-chose pour vous si vous avez quelque chose de proche d'une application d'une seule page. Si vous autorisez Rails pour gérer le routage, utilisez simplement Angular pour organiser le comportement côté client qui ne peut pas être traité par le serveur lors de la modèles.

Suis-je en train de manquer un scénario, ici? Il ne me semble pas élégant d'essayer de diviser les responsabilités de routage entre différents frameworks, même dans une grande application ... Y a-t-il un autre scénario où Turbolinks interférerait avec Angular autre que rafraîchissant la page ou naviguer vers un nouvel itinéraire?

0
josiah