Combattere AngularJS eseguendo il controller due volte


532

Capisco che AngularJS scorre alcuni codici due volte, a volte anche di più, come $watcheventi, controllando costantemente gli stati del modello ecc.

Tuttavia il mio codice:

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

//...

Viene eseguito due volte, inserendo 2 record nel mio DB. Chiaramente sto ancora imparando perché mi sbatto la testa da anni!


104
Se il controller è in esecuzione due volte, verifica di non inizializzare l'app Angular due volte (inizializzandola automaticamente con ng-appe con il bootstrap manuale). Controlla anche se hai collegato il controller a più elementi (con ng-controller).
Stewie,

7
puoi spiegare cosa intendi con "e con il bootstrap manuale"?
sport


2
Ho notato il comportamento del controller duplicato in un'app che ho ereditato durante la risoluzione di un problema con la registrazione e la visualizzazione dei log della console due volte. Il primo incendio aveva un valore, ma il secondo non era definito. Dopo aver rimosso la direttiva HTML ng-controller per il controller, il secondo incendio del log della console che era indefinito è andato via.
Daniel Nalbach,

2
se angular.js viene aggiunto due volte, anche questo può succedere
Mohit,

Risposte:


1050

Il router dell'app ha specificato la navigazione in questo MyControllermodo:

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

Ma ho anche avuto questo in home.html:

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

Questo ha digerito il controller due volte. La rimozione data-ng-controllerdell'attributo dall'HTML ha risolto il problema. In alternativa, la controller:proprietà avrebbe potuto essere rimossa dalla direttiva di routing.

Questo problema si verifica anche quando si utilizza la navigazione a schede. Ad esempio, app.jspotrebbe contenere:

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

La scheda HTML dei rapporti corrispondente potrebbe assomigliare a:

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

Ciò comporterà anche l'esecuzione del controller due volte.


4
Questo sembra il posto migliore per lasciarlo, dopo molte ore di utilizzo del mio codice, non chiedermi come, ma quando cambio <div ng-view>...a <div class="ng-view">..., questo problema si è fermato per me. Non ho mai incontrato questo comportamento prima, ma forse anche qualcun altro ha questo.
Phix,

5
C'è una buona spiegazione di ciò nei documenti per ngController, docs.angularjs.org/api/ng/directive/ngController
Charles,

1
Esistono modi per dichiarare un controller nel percorso e quindi un altro controller nella stessa pagina html? Come garantire sia il funzionamento che l'assenza di conflitti? O questo non è necessario?
Pensatore

1
Probabilmente non è necessario, potresti usare un paio di direttive invece?
Greg,

2
@Josh lasciandolo nell'HTML riduce la flessibilità. Stai collegando il tuo modello direttamente a un controller. È possibile utilizzare ctrl come sintassi con il routing.
Greg

127

AngularJS docs - ngController
Nota che puoi anche collegare controller al DOM dichiarandolo in una definizione di route tramite il servizio $ route. Un errore comune è dichiarare nuovamente il controller usando ng-controller nel modello stesso. Ciò farà sì che il controller venga collegato ed eseguito due volte.

Quando si utilizza ngRoute con la ng-viewdirettiva, il controller viene collegato all'elemento dom per impostazione predefinita (o ui-view se si utilizza ui-router). Quindi non sarà necessario ricollegarlo nel modello.


Risposta duplicata. L'autore ha già risposto dicendo lo stesso. Basta scorrere verso il basso fino alla fine della pagina.
Iago,

4
Ho trovato confuso che l'autore lo abbia inserito come nota a margine mentre è ovviamente il modo corretto di farlo. La citazione dai documenti risponde chiaramente alla domanda. E sono sicuro che molti troveranno utile il link al documento.
shxfee,

Ciò ha risolto il mio problema di esecuzione del controller due volte. Tutto quello che ho fatto è stato rimuovere il controller ng nel modello e ora viene eseguito solo una volta.
Torbenrudgaard,

70

Ho appena superato questo, ma il problema era diverso dalla risposta accettata. Lo sto davvero lasciando qui per il mio io futuro, per includere i passaggi che ho fatto per risolverlo.

  1. Rimuovere le dichiarazioni ridondanti del controller
  2. Controlla le barre finali nei percorsi
  3. Controlla ng-ifs
  4. Controlla eventuali ng-viewchiamate di wrapping non necessarie (ho lasciato per caso in una ng-viewche stava avvolgendo il mio effettivo ng-view. Ciò ha comportato tre chiamate ai miei controller.)
  5. Se sei su Rails, dovresti rimuovere la turbolinksgemma dal tuo application.jsfile. Ho sprecato un'intera giornata a scoprirlo. Risposta trovata qui .
  6. Inizializzazione dell'app due volte con ng-app e con bootstrap. Combattere AngularJS eseguendo il controller due volte
  7. Quando si usa $ compile su tutto l'elemento nella funzione 'link' della direttiva che ha anche il proprio controller definito e usa i callback di questo controller nel template tramite ng-click ecc. Trovate la risposta qui .

5. se sei su Rails, dovresti rimuovere turbolinksgem dal tuo application.jsfile. Mi ha fatto impazzire, ho perso tutto il giorno a scoprirlo. Vero dolore. Risposta trovata qui
18

6. Inizializzazione dell'app due volte con ng-app e con bootstrap. stackoverflow.com/questions/15535336/...
thadeuszlay

1
Grazie . Per me, la cosa che ha funzionato è stata tagliare la barra alla fine del percorso
Pramod Sharma,

Grazie mille, mi sono rotto la testa per questo. finito per essere il numero 7! (sempre l'ultimo ...).
Rabbi Shuki Gur,

Caspita, che ci crediate o no, ho esaminato tutto sulla tua lista e sto ancora riscontrando il problema.
Jacob Stamm,

14

Voglio solo aggiungere un altro caso quando il controller può iniziare due volte (questo è vero per angular.js 1.3.1):

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

In questo caso $ route.current sarà già impostato quando verrà avviato ng-view. Ciò causa una doppia inizializzazione.

Per risolverlo basta cambiare ng-if in ng-show / ng-hide e tutto funzionerà bene.


Mi ha aiutato, stavo usando ng-show / ng-hide invece di ng-if, avevo due ng-view, non è un problema ma ng-if disabilita mentre ng-show / ng-hide non lo fa
Dimitri Kopriwa,

Grazie mille. Questo approccio ha risolto il mio problema. Stava chiamando lo stesso ui-viewdue volte che ha chiamato il controller due volte.
Curlyreggie,

7

Vorrei aggiungere come riferimento:

L'esecuzione del doppio codice del controller può essere causata anche facendo riferimento al controller in una direttiva che funziona anche sulla pagina.

per esempio

return {

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

Quando hai anche ng-controller = "myController" nel tuo HTML


qual è la soluzione per questo?
Sagar Bhosale

1
Usa solo l'uno o l'altro.
gb2d,


6

Ho strappato la mia app e tutte le sue dipendenze a pezzi su questo problema (dettagli qui: l' app AngularJS si avvia due volte ( ho provato le solite soluzioni ..) )

E alla fine, è stata tutta colpa del plugin Batarang Chrome.

Risoluzione in questa risposta :

Consiglio vivamente che la prima cosa nell'elenco di chiunque sia disabilitarlo per post prima di modificare il codice.


Questo era il mio problema. Il plug-in esegue una doppia istanza della tua app Angular in modo che possa fornire informazioni sull'ambito (non so perché non può semplicemente usare l'istanza iniziale).
rgdigital

Sono contento che abbia aiutato, mi ci è voluto molto tempo per capirlo.
Lewis,

5

Se sai che il tuo controller sta eseguendo involontariamente più di una volta, prova a cercare tra i tuoi file il nome del controller offensivo, ad esempio: ricerca: MyController attraverso tutti i file. Probabilmente è stato copiato e incollato in qualche altro file html / js e ti sei dimenticato di cambiarlo quando hai iniziato a sviluppare o usare quei parziali / controller. Fonte: ho fatto questo errore


5

Ho avuto lo stesso problema, in una semplice app (senza routing e un semplice riferimento ng-controller) e il costruttore del mio controller ha funzionato due volte. Alla fine, ho scoperto che il mio problema era la seguente dichiarazione per l'avvio automatico della mia applicazione AngularJS nella vista Rasoio

<html ng-app="mTest1">

L'ho anche riavviato manualmente usando angular.bootstrap ie

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

così rimuovendo uno di loro, ha funzionato per me.


4

In alcuni casi la direttiva viene eseguita due volte quando semplicemente non si corregge la chiusura della direttiva in questo modo:

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

Questo eseguirà la direttiva due volte. Inoltre, c'è un altro caso in cui la direttiva viene eseguita due volte:

assicurati di non includere la tua direttiva nel tuo index.html TWICE !


Inoltre, se si utilizza UI-Router, specificare il nome della direttiva per la vista utente all'interno di una classe sembra comportare una doppia esecuzione. La modifica in E o <ui-view> </ui-view> risolve il problema della doppia esecuzione.
SteveGSD,

2

Mi sono grattato la testa su questo problema con AngularJS 1.4 rc build, quindi ho realizzato che nessuna delle risposte sopra era applicabile poiché era originata dalla nuova libreria router per Angular 1.4 e Angular 2 al momento della stesura di questo articolo. Pertanto, sto rilasciando una nota qui per chiunque possa utilizzare la nuova libreria di percorsi angolari.

Fondamentalmente se una pagina html contiene una ng-viewportdirettiva per il caricamento di parti della tua app, facendo clic su un collegamento ipertestuale specificato in si ng-linkfarebbe caricare due volte il controller di destinazione del componente associato. La sottile differenza è che, se il browser ha già caricato il controller di destinazione, facendo nuovamente clic sullo stesso collegamento ipertestuale si invocherebbe il controller solo una volta.

Non ho ancora trovato una soluzione alternativa, anche se credo che questo comportamento sia coerente con l'osservazione sollevata da shaunxu , e spero che questo problema venga risolto nella futura build della nuova libreria di rotte e insieme alle versioni di AngularJS 1.4.


2

Nel mio caso, ho trovato due viste usando lo stesso controller.

$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

Il problema che sto riscontrando potrebbe essere tangenziale, ma poiché googling mi ha portato a questa domanda, questo potrebbe essere appropriato. Il problema suscita la sua brutta testa per me quando utilizzo UI Router, ma solo quando provo ad aggiornare la pagina con il pulsante di aggiornamento del browser. L'app utilizza il router UI con uno stato astratto padre e quindi gli stati figlio fuori dal padre. Sulla run()funzione app , c'è un $state.go('...child-state...')comando. Lo stato genitore utilizza un resolve, e all'inizio pensavo che forse un controller figlio eseguisse due volte.

Va tutto bene prima che all'URL sia stato aggiunto l'hash.
www.someoldwebaddress.org

Quindi, una volta che l'URL è stato modificato dal router dell'interfaccia utente,
www.someoldwebaddress.org#/childstate

... e poi quando aggiorno la pagina con il pulsante di aggiornamento del browser , il $stateChangeStartfuoco si accende due volte e ogni volta punta al childstate.

Lo resolvestato genitore è ciò che sta sparando due volte.

Forse questo è un kludge; a prescindere, questo sembra eliminare il problema per me: nell'area del codice in cui $stateProviderviene invocato per la prima volta , per prima cosa controlla se window.location.hash è una stringa vuota. Se lo è, va tutto bene; in caso contrario, impostare window.location.hash su una stringa vuota. Quindi sembra che l' $stateunico tentativo di andare da qualche parte una volta anziché due volte.

Inoltre, se non vuoi fare affidamento sul valore predefinito dell'app rune state.go(...), puoi provare a catturare il valore hash e utilizzare il valore hash per determinare lo stato figlio in cui ti trovavi poco prima dell'aggiornamento della pagina e aggiungere una condizione all'area nella tua codice dove hai impostato il state.go(...).


1

Nel mio caso è stato a causa del pattern url che ho usato

il mio url era come / ui / project /: parametro1 /: parametro2.

Non avevo bisogno di paramerter2 in tutti i casi di cambio di stato. Nel caso in cui non avessi bisogno del secondo parametro, il mio URL sarebbe come / ui / project /: parametro1 /. E così ogni volta che ho avuto un cambio di stato avrò il mio controller aggiornato due volte.

La soluzione era impostare parametro2 come stringa vuota e fare il cambio di stato.


1

Ho avuto questa doppia inizializzazione per un motivo diverso. Per alcune transizioni di percorso nella mia applicazione, volevo forzare lo scorrimento verso la parte superiore della pagina (ad es. Nei risultati di ricerca impaginati ... facendo clic su Avanti dovresti portarti all'inizio della pagina 2).

L'ho fatto aggiungendo un ascoltatore al $rootScope $on $viewContentLoadedquale (in base a determinate condizioni) eseguito

$location.hash('top');

Inavvertitamente questo stava facendo rivalutare i miei percorsi e reinizializzare i controller



1

Nel mio caso, rinominare il controller con un nome diverso ha risolto il problema.

Si è verificato un conflitto tra i nomi dei controller con il modulo " angular -ui-tree ": ho rinominato il mio controller da "CatalogerTreeController" a " TreeController " e quindi questo controller inizia ad essere avviato due volte nella pagina in cui la direttiva " ui-tree " ha utilizzato perché questa direttiva usa un controller chiamato " TreeController ".


1

Per coloro che utilizzano la sintassi ControllerAs, dichiarare l'etichetta del controller in $ routeprovider come segue:

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

o

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

Dopo aver dichiarato $ routeprovider, non fornire il controller come nella vista. Usa invece l'etichetta nella vista.


0

Ho avuto lo stesso problema e dopo aver provato tutte le risposte ho finalmente scoperto che avevo una direttiva a mio avviso legata allo stesso controller.

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

Dopo aver rimosso la direttiva, il controller ha smesso di essere chiamato due volte. Ora la mia domanda è: come può utilizzare questa direttiva legata all'ambito del controller senza questo problema?


0

Ho appena risolto il mio, che in realtà è stato piuttosto deludente. È un'app ibrida ionica, ho usato ui-router v0.2.13. Nel mio caso c'è un lettore epub (che utilizza epub.js) che riportava continuamente "nessun documento trovato" una volta che mi trovo nella libreria dei miei libri e seleziono qualsiasi altro libro. Quando ho ricaricato il libro del browser veniva reso perfettamente, ma quando ho selezionato un altro libro ha avuto di nuovo lo stesso problema.

La mia soluzione è stata molto semplice. Ho appena rimosso reload:trueda $state.go("app.reader", { fileName: fn, reload: true });nel mioLibraryController


0

Ho lo stesso problema in angular-route@1.6.7, e perché la barra extra alla fine del percorso regex:

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

per

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

e funziona correttamente.


0

Anche qui aggiungo il mio caso:

Stavo usando angular-ui-router con$state.go('new_state', {foo: "foo@bar"})

Una volta ho aggiunto encodeURIComponent al parametro, il problema era sparito: $state.go('new_state', {foo: encodeURIComponent("foo@bar")}).

Quello che è successo? Il carattere "@" nel valore del parametro non è consentito negli URL. Di conseguenza, angular-ui-router ha creato il mio controller due volte: durante la prima creazione ha passato l'originale "foo @ bar", durante la seconda creazione avrebbe passato la versione codificata "foo% 40bar". Dopo aver codificato esplicitamente il parametro come mostrato sopra, il problema è scomparso.


-1

Ho capito che il mio è stato chiamato due volte perché stavo chiamando il metodo due volte dal mio 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 sezione evidenziata faceva sì che findX () venisse chiamato due volte. Spero che aiuti qualcuno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.