Combattre le contrôleur exécutant AngularJS deux fois

532

Je comprends que AngularJS exécute deux fois du code, parfois plus, comme des $watchévénements, vérifiant constamment les états du 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 clairement encore car je me suis cogné la tête contre cela depuis des lustres!

Greg
la source
104
Si votre contrôleur fonctionne deux fois, vérifiez que vous n'initialisez pas votre application Angular deux fois (en l'initialisant automatiquement avec ng-appet avec un bootstrap manuel). Vérifiez également si vous avez attaché votre contrôleur à plusieurs éléments (avec ng-controller).
Stewie
7
pouvez-vous expliquer ce que vous entendez par "et avec bootstrap manuel"?
sports
2
J'ai remarqué le comportement de contrôleur en double dans une application dont j'ai hérité lors du dépannage d'un problème de journalisation et lorsque les journaux de la console se déclenchent deux fois. Le premier feu de bois avait une valeur, mais le second n'était pas défini. Après avoir supprimé la directive HTML ng-controller pour le contrôleur, le deuxième incendie de journal de la console non défini a disparu.
Daniel Nalbach
2
si angular.js est ajouté deux fois, alors cela peut également arriver
Mohit

Réponses:

1050

Le routeur d'application a spécifié la navigation MyControllercomme suit:

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

Mais j'ai aussi eu ça dans home.html:

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

Cela a digéré le contrôleur deux fois. La suppression de l' data-ng-controllerattribut du code HTML a résolu le problème. Alternativement, la controller:propriété 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.jspeut contenir:

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

L'onglet HTML des rapports correspondant peut ressembler à:

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

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

Greg
la source
4
Cela semble le meilleur endroit pour laisser cela, après plusieurs heures de peignage de mon code, ne me demandez pas comment, mais lorsque je passe <div ng-view>...à <div class="ng-view">..., ce problème s'est arrêté pour moi. Je n'ai jamais rencontré ce comportement auparavant, mais peut-être que quelqu'un d'autre l'a aussi.
Phix
5
Il y a une bonne explication de cela dans les documents pour ngController, docs.angularjs.org/api/ng/directive/ngController
Charles
1
Existe-t-il des moyens de déclarer un contrôleur dans la route, puis un autre contrôleur sur la page html elle-même? Comment garantir à la fois le fonctionnement et l'absence de conflits? Ou est-ce inutile?
Penseur
1
C'est probablement inutile, utilisez peut-être quelques directives à la place?
Greg
2
@Josh le laisser dans le HTML réduit la flexibilité. Vous liez votre modèle directement à un contrôleur. Vous pouvez utiliser ctrl comme syntaxe avec le routage.
Greg
127

AngularJS docs - ngController
Notez que vous pouvez également attacher des contrôleurs au DOM en le déclarant dans une définition de route via le service $ route. Une erreur courante consiste à déclarer à nouveau le contrôleur à l'aide de 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 ng-viewdirective, 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.

shxfee
la source
Réponse dupliquée. L'auteur a déjà répondu en disant la même chose. Faites défiler vers le bas jusqu'à la fin de la page.
Iago
4
J'ai trouvé confus que l'auteur l'ait mis en note alors que c'est évidemment la bonne façon de le faire. La citation de la documentation répond clairement à la question. Et je suis sûr que beaucoup trouveraient le lien vers le document utile.
shxfee
Cela a résolu mon problème de l'exécution du contrôleur deux fois. Tout ce que j'ai fait était de supprimer le ng-controller dans le modèle et maintenant il ne s'exécute qu'une seule fois.
torbenrudgaard
70

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érifier les barres obliques de fin dans les itinéraires
  3. Vérifier ng-ifs
  4. Vérifiez tous les ng-viewappels d' emballage inutiles (je l'avais accidentellement laissé dans un ng-viewqui enveloppait mon réel ng-view. Cela a entraîné trois appels à mes contrôleurs.)
  5. Si vous êtes sur Rails, vous devez supprimer la turbolinksgemme de votre application.jsfichier. J'ai perdu une journée entière à découvrir ça. Réponse trouvée ici .
  6. Initialisation de l'application deux fois avec ng-app et avec bootstrap. Combattre le contrôleur exécutant AngularJS deux fois
  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 .
JesseBuesking
la source
5. Si vous êtes sur Rails, vous devez supprimer la turbolinksgemme de votre application.jsfichier. Cela m'a rendu fou, perdu toute la journée à découvrir ça. Vraie douleur. Réponse trouvée ici
18augst
6. Initialisation de l'application deux fois avec ng-app et avec bootstrap. stackoverflow.com/questions/15535336/…
thadeuszlay
1
Merci . Pour moi, la chose qui a fonctionné a été de mettre du slash à la fin du parcours
Pramod Sharma
Merci mille fois, ça me brisait la tête. a fini par être le numéro 7! (toujours le dernier ...).
Rabbi Shuki Gur
Wow, croyez-le ou non, j'ai parcouru tout sur votre liste, et j'ai toujours le problème.
Jacob Stamm
14

Je veux juste ajouter un cas de plus lorsque le contrôleur peut initier deux fois (c'est le cas 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 lorsque ng-view sera lancé. Cela provoque une double initialisation.

Pour le corriger, changez simplement ng-if en ng-show / ng-hide et tout fonctionnera bien.

DontRelaX
la source
Cela m'a aidé, j'utilisais ng-show / ng-hide au lieu de ng-if, j'avais deux ng-view, ce n'est pas un problème mais ng-if est désactivé alors que ng-show / ng-hide ne fonctionne pas
Dimitri Kopriwa
Merci beaucoup. Cette approche a résolu mon problème. Appelait le même ui-viewdeux fois, ce qui a appelé le contrôleur deux fois.
curlyreggie
7

Je 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

gb2d
la source
Quelle est la solution pour cela?
Sagar Bhosale
1
N'utilisez que l'un ou l'autre.
gb2d
7

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

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

fracz
la source
6

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

Et à la fin, tout était la faute du plugin Batarang Chrome.

Résolution dans cette réponse :

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

Lewis
la source
C'était mon problème. Le plugin exécute une double instance de votre application Angular afin qu'il puisse fournir des informations de portée (je ne sais pas pourquoi il ne peut pas simplement utiliser l'instance initiale).
rgdigital
Je suis content que cela ait aidé, il m'a fallu beaucoup de temps pour comprendre cela.
Lewis
5

Si vous savez que votre contrôleur exécute involontairement plus d'une fois, essayez une recherche dans vos fichiers pour le nom du contrôleur incriminé, ex: search: MyController dans tous les fichiers. Il a probablement été copié-collé dans un autre fichier html / js et vous avez oublié de le changer lorsque vous avez commencé à développer ou à utiliser ces partiels / contrôleurs. Source: j'ai fait cette erreur

user3901016
la source
5

J'ai eu le même problème, dans une application simple (sans routage et avec une simple référence ng-controller) et le constructeur de mon contrôleur a fonctionné deux fois. Enfin, j'ai découvert que mon problème était la déclaration suivante pour amorcer automatiquement mon application AngularJS dans ma vue Razor

<html ng-app="mTest1">

Je l'ai également démarré manuellement en utilisant angular.bootstrap ie

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

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

athina.bikaki
la source
4

Dans certains cas, votre directive s'exécute deux fois lorsque vous ne corrigez simplement pas la directive close close comme ceci:

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

Cela exécutera votre directive deux fois. Il existe également un autre cas fréquent où votre directive s'exécute deux fois:

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

pleerock
la source
De plus, si vous utilisez UI-Router, la spécification du nom de la directive pour l'interface utilisateur dans une classe semble entraîner une double exécution. Le changer en E ou <ui-view> </ui-view> corrige le problème de double exécution.
SteveGSD
2

Je me suis gratté la tête sur ce problème avec AngularJS 1.4 rc build, puis j'ai réalisé 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.

Fondamentalement, si une page html contient une ng-viewportdirective pour charger des parties de votre application, en cliquant sur un lien hypertexte spécifié dans avec ng-link, le contrôleur cible du composant associé serait chargé deux fois. La différence subtile est que, si le navigateur a déjà chargé le contrôleur cible, en re-cliquant sur le même lien hypertexte, le contrôleur ne sera invoqué qu'une seule fois.

Je n'ai pas encore trouvé de solution de contournement viable, bien que je pense que ce comportement est cohérent avec l'observation soulevée par shaunxu , et j'espère que ce problème sera résolu dans la future construction de la nouvelle bibliothèque de routes et avec les versions d'AngularJS 1.4.

codeful.element
la source
2

Dans mon cas, j'ai trouvé deux vues en 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'
    }
  }
});
Luke Eller
la source
2

Le problème que je rencontre peut être tangentiel, mais comme la recherche sur Google m'a amené à cette question, cela pourrait être approprié. Le problème soulève sa tête laide pour moi lors de l'utilisation du routeur UI, mais uniquement lorsque j'essaie de rafraîchir la page avec le bouton de rafraîchissement du navigateur. L'application utilise UI Router avec un état abstrait parent, puis les états enfants hors parent. Sur la run()fonction d' application , il y a une $state.go('...child-state...')commande. L'état parent utilise un 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

Ensuite, une fois que l'URL a été modifiée par UI Router,
www.someoldwebaddress.org#/childstate

... puis lorsque je rafraîchis la page avec le bouton d'actualisation du navigateur , le système se $stateChangeStartdéclenche deux fois et pointe à chaque fois sur le childstate.

Le resolvesur l'état parent est ce qui se déclenche deux fois.

C'est peut-être un coup de coude; quoi qu'il en soit, cela semble éliminer le problème pour moi: dans la zone de code où $stateProviderest d'abord invoqué, vérifiez d' abord 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 $stateseul essaie d'aller quelque part une fois plutôt que deux fois.

De plus, si vous ne souhaitez pas vous fier à la valeur par défaut de l'application runet state.go(...), vous pouvez essayer de capturer la valeur de hachage et utiliser la valeur de hachage pour déterminer l'état enfant sur lequel vous étiez juste avant l'actualisation de la page et ajouter une condition à la zone de votre code où vous définissez le state.go(...).

mg1075
la source
1

Dans mon cas, c'était à cause du modèle d'URL que j'utilisais

mon URL était comme / ui / project /: parameter1 /: parameter2.

Je n'ai pas eu besoin de paramerter2 dans tous les cas de changement d'état. Dans les cas où je n'ai pas eu besoin du deuxième paramètre, mon URL serait comme / ui / project /: parameter1 /. 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 à effectuer le changement d'état.

Sherin Syriac
la source
1

J'ai eu cette double initialisation pour une raison différente. Pour certaines transitions d'itinéraire dans mon application, je voulais forcer le défilement vers le haut de la page (par exemple, dans les résultats de recherche paginés ... cliquer sur Suivant devrait vous amener au haut de la page 2).

Je l'ai fait en ajoutant un écouteur à celui $rootScope $on $viewContentLoadedqui (en fonction de certaines conditions) a exécuté

$location.hash('top');

Par inadvertance, cela a entraîné une réévaluation de mes itinéraires et une réinitialisation des contrôleurs

Harry Lime
la source
1

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

Il y a eu 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 lancé deux fois sur la page où la directive " ui-tree " est utilisée car cette directive utilise un contrôleur nommé " TreeController ".

AlexDEV.pro
la source
1

Pour ceux qui utilisent la syntaxe ControllerAs, déclarez simplement l'étiquette du contrôleur dans $ routeprovider 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é $ routeprovider, ne fournissez pas le contrôleur comme dans la vue. Utilisez plutôt l'étiquette dans la vue.

M. Arnold
la source
0

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 é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. Maintenant ma question est, comment peut utiliser cette directive liée à la portée du contrôleur sans ce problème?

Daniel Vilas-Boas
la source
0

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 y a un lecteur epub (utilisant epub.js) qui signalait continuellement "aucun document trouvé" une fois que je naviguais vers ma bibliothèque de livres et sélectionnais n'importe quel autre livre. Lorsque j'ai rechargé, le livre du navigateur était rendu parfaitement, mais lorsque j'ai sélectionné un autre livre, il a de nouveau rencontré le même problème.

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

maksbd19
la source
0

J'ai le même problème [email protected], et cela parce que la barre oblique supplémentaire à la fin de la route regex:

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

à

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

et cela fonctionne correctement.

B.Ma
la source
0

J'ajoute juste mon cas ici aussi:

J'utilisais angular-ui-router avec$state.go('new_state', {foo: "foo@bar"})

Une fois que j'ajouté encodeURIComponent au paramètre, le problème a disparu: $state.go('new_state', {foo: encodeURIComponent("foo@bar")}).

Qu'est-il arrivé? Le caractère "@" dans la valeur du paramètre n'est pas autorisé dans les URL. En conséquence, angular-ui-router a créé mon contrôleur deux fois: lors de la première création, il a passé le "foo @ bar" d'origine, lors de la deuxième création, il a transmis la version codée "foo% 40bar". Une fois que j'ai codé explicitement le paramètre comme indiqué ci-dessus, le problème a disparu.

À M
la source
-1

J'ai compris que la mienne était appelée deux fois parce que j'appelais la méthode deux fois depuis mon html.

`<form class="form-horizontal" name="x" ng-submit="findX() novalidate >
 <input type="text"....>
 <input type="text"....>
 <input type="text"....>
 <button type="submit" class="btn btn-sm btn-primary" ng-click="findX()"
</form>`

La section en surbrillance provoquait l'appel de findX () deux fois. J'espère que cela aide quelqu'un.

Nandu Prajapati
la source