web-dev-qa-db-fra.com

Combattre deux fois le contrôleur d'exécution AngularJS

Je comprends qu'AngularJS parcourt du code deux fois, parfois même davantage, comme les événements $watch, la vérification constante des états de modèle, etc.

Cependant mon code:

function MyController($scope, User, local) {

var $scope.User = local.get(); // Get locally save user data

User.get({ id: $scope.User._id.$oid }, function(user) {
  $scope.User = new User(user);
  local.save($scope.User);
});

//...

Est exécuté deux fois, insérant 2 enregistrements dans ma base de données. J'apprends encore clairement car je me frappe la tête depuis des siècles!

519
Greg

Le routeur de l'application a spécifié la navigation à MyController comme ceci:

$routeProvider.when('/',
                   { templateUrl: 'pages/home.html',
                     controller: MyController });

Mais j'avais aussi ceci dans home.html:

<div data-ng-controller="MyController">

Cette digéré le contrôleur deux fois. La suppression de l'attribut data-ng-controller du code HTML a résolu le problème. Sinon, la propriété controller: aurait pu être supprimée de la directive de routage.

Ce problème apparaît également lors de l'utilisation de la navigation par onglets. Par exemple, app.js peut contenir:

  .state('tab.reports', {
    url: '/reports',
    views: {
      'tab-reports': {
        templateUrl: 'templates/tab-reports.html',
        controller: 'ReportsCtrl'
      }
    }
  })

L'onglet HTML correspondant aux rapports pourrait ressembler à:

<ion-view view-title="Reports">
  <ion-content ng-controller="ReportsCtrl">

Cela entraînera également l'exécution du contrôleur deux fois.

1030
Greg

Documents AngularJS - ngController
Notez que vous pouvez également attacher des contrôleurs au DOM en le déclarant dans une définition d'itinéraire via le service $ route. Une erreur courante est de déclarer à nouveau le contrôleur en utilisant ng-controller dans le modèle lui-même. Cela entraînera la connexion et l'exécution du contrôleur deux fois.

Lorsque vous utilisez ngRoute avec la directive ng-view, le contrôleur est attaché à cet élément dom par défaut (ou ui-view si vous utilisez ui-router). Vous n'aurez donc pas besoin de l'attacher à nouveau dans le modèle.

124
shxfee

Je viens de passer par là, mais le problème était différent de la réponse acceptée. Je laisse vraiment cela ici pour mon avenir, pour inclure les étapes que j'ai suivies pour le réparer.

  1. Supprimer les déclarations de contrôleur redondantes
  2. Vérifiez les barres obliques dans les itinéraires
  3. Vérifier pour ng-ifs
  4. Vérifiez qu'il n'y a pas d'appels ng-view d'encapsulation inutiles (j'avais accidentellement laissé un ng-view qui encapsulait mon ng-view réel. Cela a entraîné trois appels vers mes contrôleurs.)
  5. Si vous êtes sur Rails, vous devez supprimer la gemme turbolinks de votre fichier application.js. J'ai perdu toute une journée pour découvrir cela. Réponse trouvée ici .
  6. Initialiser l'application deux fois avec ng-app et avec bootstrap. Lutte contre le contrôleur d'exécution AngularJS à deux reprises
  7. Lorsque vous utilisez $ compile sur un élément entier dans la fonction 'link' de la directive qui a également son propre contrôleur défini et utilise les rappels de ce contrôleur dans le modèle via ng-click, etc. Réponse trouvée ici .
64
JesseBuesking

Je veux juste ajouter un cas de plus quand le contrôleur peut initier deux fois (c'est vrai pour angular.js 1.3.1):

<div ng-if="loading">Loading...</div>
<div ng-if="!loading">
    <div ng-view></div>
</div>

Dans ce cas, $ route.current sera déjà défini quand ng-view sera initié. Cela cause une double initialisation.

Pour résoudre ce problème, il suffit de changer ng-if en ng-show/ng-hide et tout fonctionnera bien.

12
DontRelaX

Voudrais ajouter pour référence:

L'exécution de code de contrôleur double peut également être provoquée en référençant le contrôleur dans une directive qui s'exécute également sur la page. 

par exemple.

return {

            restrict: 'A',
            controller: 'myController',
            link: function ($scope) { ....

Lorsque vous avez également ng-controller = "myController" dans votre code HTML

7
gb2d

Lors de l’utilisation de angular-ui-router avec Angular 1.3+, il existait un problème concernant Le rendu des vues deux fois lors de la transition d’itinéraire . Cela a abouti à l'exécution de contrôleurs à deux reprises également. Aucune des solutions proposées n'a fonctionné pour moi.

Cependant, la mise à jour de angular-ui-router de 0.2.11 à 0.2.13 a résolu le problème pour moi.

7
fracz

J'ai déchiré mon application et toutes ses dépendances en bits sur ce problème (détails ici: L'application AngularJS démarrant deux fois (essayé les solutions habituelles ..) )

Et finalement, tout était de la faute du plugin Batarang Chrome.

Résolution dans cette réponse :

Je recommande fortement que la première chose sur la liste de quiconque est de le désactiver par la poste avant de modifier le code. 

6
Lewis

J'ai eu le même problème, dans une application simple (sans routage et une simple référence ng-controller) et le constructeur de mon contrôleur a été exécuté deux fois. Enfin, j'ai découvert que mon problème était la déclaration suivante qui consistait à auto-démarrer mon application AngularJS dans ma vue Razor.

<html ng-app="mTest1">

Je l'ai aussi démarré manuellement en utilisant angular.bootstrap i.e.

angular.bootstrap(document, [this.app.name]);

donc enlever l'un d'entre eux, ça a fonctionné pour moi.

5
athina.bikaki

Si vous savez que votre contrôleur s'exécute involontairement plus d'une fois, essayez de rechercher dans le fichier le nom du contrôleur incriminé, par exemple: search: MyController dans tous les fichiers. Il est probable qu’il a été copié-collé dans un autre fichier html/js et que vous avez oublié de le changer lorsque vous avez développé ou utilisé ces partiels/contrôleurs. Source: j'ai commis cette erreur

4
user3901016

Dans certains cas, votre directive est exécutée deux fois lorsque vous ne corrigez tout simplement pas votre directive, comme ceci:

<my-directive>Some content<my-directive>

Cela va exécuter votre directive deux fois ... Il y a aussi un autre cas souvent où votre directive est exécutée deux fois:

assurez-vous de ne pas inclure votre directive dans votre index.htmlDEUX FOIS!

4
pleerock

Le problème que je rencontre est peut-être tangentiel, mais comme Google m'a amené à cette question, cela pourrait être approprié. Le problème me fait mal à la tête lorsque j'utilise un routeur d'interface utilisateur, mais uniquement lorsque je tente d'actualiser la page avec le bouton d'actualisation du navigateur. L'application utilise le routeur d'interface utilisateur avec un état abstrait parent, puis les états enfant du parent. Sur la fonction app run(), il existe une commande $state.go('...child-state...'). Le parent state utilise resolve, et au début, je pensais qu'un contrôleur enfant s'exécutait deux fois.

Tout va bien avant que l'URL ait ajouté le hachage.
www.someoldwebaddress.org

Puis, une fois l’URL modifiée par le routeur UI,
www.someoldwebaddress.org#/childstate

... et ensuite, lorsque je actualise la page avec le bouton d'actualisation du navigateur, le $stateChangeStart se déclenche deux fois et chaque fois pointe sur la childstate.

La resolve de l'état parent est ce qui se déclenche deux fois.

Peut-être que c'est un kludge; peu importe, cela semble éliminer le problème pour moi: dans la zone de code où $stateProvider est d'abord appelé, first vérifie si le window.location.hash est une chaîne vide. Si c'est le cas, tout va bien; Si ce n'est pas le cas, définissez window.location.hash sur une chaîne vide. Ensuite, il semble que le $state ne tente d'aller quelque part qu'une fois plutôt que deux fois.

De même, si vous ne souhaitez pas utiliser les valeurs par défaut run et state.go(...) par défaut de l'application, vous pouvez essayer de capturer la valeur de hachage et de l'utiliser pour déterminer l'état de l'enfant sur lequel vous vous trouviez juste avant l'actualisation de la page et ajouter une condition à la zone. dans votre code où vous définissez la state.go(...).

2
mg1075

J'étais en train de me gratter la tête à propos de ce problème avec AngularJS 1.4 rc build, puis je me suis rendu compte qu’aucune des réponses ci-dessus n’était applicable, car elle provenait de la nouvelle bibliothèque de routeurs pour Angular 1.4 et Angular 2 au moment de la rédaction de cet article. Par conséquent, je laisse tomber une note ici pour toute personne qui pourrait utiliser la nouvelle bibliothèque de routes angulaires.

En gros, si une page html contient une directive ng-viewport pour le chargement de parties de votre application, un clic sur un lien hypertexte spécifié dans avec ng-link entraînerait le chargement du contrôleur cible du composant associé deux fois. La différence subtile est que, si le navigateur a déjà chargé le contrôleur cible, un nouveau clic sur le même lien hypertexte n’appellera le contrôleur qu’une fois. 

Nous n’avons pas encore trouvé de solution viable, même si j’estime que ce comportement est conforme à l’observation soulevée par shaunxu , et nous espérons que ce problème sera résolu dans l’avenir, avec la nouvelle bibliothèque de routes et les versions d’AngularJS 1.4. 

2
codeful.element

Dans mon cas, j'ai trouvé deux vues utilisant le même contrôleur.

$stateProvider.state('app', {
  url: '',
  views: {
    "viewOne@app": {
      controller: 'CtrlOne as CtrlOne',
      templateUrl: 'main/one.tpl.html'
    },
    "viewTwo@app": {
      controller: 'CtrlOne as CtrlOne',
      templateUrl: 'main/two.tpl.html'
    }
  }
});
2
Luke Eller

Cette double initialisation a eu lieu pour une raison différente. Pour certaines transitions de route dans mon application, je souhaitais forcer le défilement vers le haut de la page (par exemple, dans les résultats de recherche paginés ... un clic sur Suivant devrait vous amener au haut de la page 2).

J'ai fait cela en ajoutant un écouteur au $rootScope$on$viewContentLoaded qui (en fonction de certaines conditions) a exécuté

$location.hash('top');

Par inadvertance, mes routes ont été réévaluées et les contrôleurs ont été réinitialisés.

1
Harry Lime

Mon problème mettait à jour les paramètres de recherche comme so $location.search('param', key);

vous pouvez en lire plus ici

le contrôleur se fait appeler deux fois pour ajouter des paramètres dans l'URL

1
Gal Bracha

Dans mon cas, c'était à cause du motif d'URL que j'ai utilisé

mon URL était comme/ui/projet /: paramètre1 /: paramètre2. 

Je n'ai pas eu besoin de paramerter2 dans tous les cas de changement d'état. Dans les cas où je n'avais pas besoin du second paramètre, mon URL serait comme/ui/projet /: paramètre1 /. Et donc chaque fois que j'ai eu un changement d'état, mon contrôleur sera rafraîchi deux fois.

La solution consistait à définir le paramètre 2 en tant que chaîne vide et à modifier l'état.

1
Sherin Syriac

Dans mon cas, renommer le contrôleur sous un nom différent a résolu le problème.

Il y avait un conflit de noms de contrôleur avec le module "angular-ui-tree": j'ai renommé mon contrôleur de "CatalogerTreeController" en "TreeController", puis ce contrôleur commence à être initiée deux fois sur la page où la directive "ui-tree" est utilisée car cette directive utilise le contrôleur nommé "TreeController".

1
AlexDEV.pro

J'ai eu le même problème et après avoir essayé toutes les réponses, j'ai finalement trouvé que j'avais une directive qui, à mon avis, était liée au même contrôleur.

APP.directive('MyDirective', function() {
  return {
    restrict: 'AE',
    scope: {},
    templateUrl: '../views/quiz.html',
    controller: 'ShowClassController'
}
});

Après avoir supprimé la directive, le contrôleur a cessé d’être appelé deux fois. Ma question est la suivante: comment utiliser cette directive liée à la portée du contrôleur sans ce problème?

0
Daniel Vilas-Boas

Je viens de résoudre le mien, ce qui était en fait assez décevant. C'est une application hybride ionique, j'ai utilisé ui-router v0.2.13. Dans mon cas, il existe un lecteur epub (utilisant epub.js) qui signalait en permanence "aucun document trouvé" une fois que je me rendais dans ma bibliothèque de livres et sélectionnais un autre livre. Lorsque j'ai rechargé le navigateur, le rendu du navigateur était parfait, mais lorsque j'ai sélectionné un autre livre, le même problème s'est à nouveau posé.

Ma résolution était très simple. Je viens de retirer reload:true de $state.go("app.reader", { fileName: fn, reload: true }); dans ma LibraryController

0
maksbd19

J'ai le même problème dans [email protected], et cela parce que le extra slash à la fin de la route regex

.when('/goods/publish/:classId/', option)

à

.when('/goods/publish/:classId', option)

et cela fonctionne correctement.

0
B.Ma

Pour ceux qui utilisent la syntaxe ControllerAs, déclarez simplement l'étiquette du contrôleur dans le fournisseur $ route comme suit:

$routeprovider
        .when('/link', {
            templateUrl: 'templateUrl',
            controller: 'UploadsController as ctrl'
        })

ou

$routeprovider
        .when('/link', {
            templateUrl: 'templateUrl',
            controller: 'UploadsController'
            controllerAs: 'ctrl'
        })

Après avoir déclaré le fournisseur $ route, ne fournissez pas le contrôleur comme dans la vue. Utilisez plutôt l'étiquette dans la vue.

0
M. Arnold