Combatiendo AngularJS ejecutando el controlador dos veces

532

Entiendo que AngularJS ejecuta algunos códigos dos veces, a veces incluso más, como $watcheventos, comprobando constantemente los estados del modelo, etc.

Sin embargo mi código:

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

//...

Se ejecuta dos veces, insertando 2 registros en mi DB. ¡Claramente todavía estoy aprendiendo ya que he estado golpeando mi cabeza contra esto por años!

Greg
fuente
104
Si su controlador se ejecuta dos veces, compruebe que no está inicializando su aplicación Angular dos veces (haciéndola inicializar automáticamente con ng-appy con arranque manual). Compruebe también si ha conectado su controlador a múltiples elementos (con ng-controller).
Stewie
77
¿Puedes explicar qué quieres decir con "y con bootstrap manual"?
deportes
2
Noté el comportamiento del controlador duplicado en una aplicación que heredé cuando solucioné un problema con el registro y vi que los registros de la consola se activaban dos veces. El primer fuego de registro tenía un valor, pero el segundo no estaba definido. Después de eliminar la directiva HTML ng-controller para el controlador, el segundo fuego de registro de la consola que estaba indefinido desapareció.
Daniel Nalbach
2
si angular.js se agrega dos veces, entonces esto también puede suceder
Mohit

Respuestas:

1050

El enrutador de la aplicación especificó la navegación para MyControllergustar así:

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

Pero también tuve esto en home.html:

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

Esto digirió el controlador dos veces. Eliminar el data-ng-controlleratributo del HTML resolvió el problema. Alternativamente, la controller:propiedad podría haberse eliminado de la directiva de enrutamiento.

Este problema también aparece cuando se usa la navegación con pestañas. Por ejemplo, app.jspodría contener:

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

El HTML de la pestaña de informes correspondiente podría parecerse a:

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

Esto también dará como resultado la ejecución del controlador dos veces.

Greg
fuente
44
Este parece ser el mejor lugar para salir de esto, después de muchas horas de peinar mi código, no me preguntes cómo, pero cuando cambio <div ng-view>...a <div class="ng-view">..., este problema se detuvo para mí. No me he encontrado con este comportamiento antes, pero tal vez alguien más lo tenga también.
Phix
55
Hay una buena explicación de esto en los documentos para ngController, docs.angularjs.org/api/ng/directive/ngController
Charles
1
¿Hay alguna manera de declarar un controlador en la ruta y luego otro controlador en la página html? ¿Cómo garantizar que funcione tanto y no haya conflictos? ¿O es esto innecesario?
Thinkerer
1
Probablemente sea innecesario, ¿tal vez use un par de directivas en su lugar?
Greg
2
@Josh dejarlo en el HTML reduce la flexibilidad. Estás vinculando tu plantilla directamente a un controlador. Puede usar ctrl como sintaxis con enrutamiento.
Greg
127

Documentos de AngularJS: ngController
Tenga en cuenta que también puede adjuntar controladores al DOM declarándolo en una definición de ruta a través del servicio $ route. Un error común es declarar el controlador nuevamente usando ng-controller en la plantilla misma. Esto hará que el controlador se conecte y ejecute dos veces.

Cuando usa ngRoute con la ng-viewdirectiva, el controlador se adjunta a ese elemento dom por defecto (o ui-view si usa ui-router). Por lo tanto, no necesitará volver a adjuntarlo en la plantilla.

shxfee
fuente
Respuesta duplicada. El autor ya respondió diciendo lo mismo. Simplemente desplácese hacia abajo hasta el final de la página.
Iago
44
Me pareció confuso que el autor lo pusiera como una nota al margen, mientras que obviamente es la forma correcta de hacerlo. La cita de los documentos responde a la pregunta claramente. Y estoy seguro de que muchos encontrarán útil el enlace al documento.
shxfee
Esto resolvió mi problema de que el controlador se ejecutara dos veces. Todo lo que hice fue eliminar el controlador ng de la plantilla y ahora solo se ejecuta una vez.
torbenrudgaard
70

Acabo de pasar por esto, pero el problema era diferente de la respuesta aceptada. Realmente estoy dejando esto aquí para mi futuro yo, para incluir los pasos que seguí para solucionarlo.

  1. Eliminar declaraciones de controlador redundantes
  2. Verifique las barras diagonales en las rutas
  3. Comprobar ng-ifs
  4. Verifique si hay ng-viewllamadas de ajuste innecesarias (accidentalmente me había dejado una ng-viewque estaba envolviendo mi actual ng-view. Esto resultó en tres llamadas a mis controladores).
  5. Si estás en Rails, debes eliminar la turbolinksgema de tu application.jsarchivo. Perdí un día entero para descubrir eso. Respuesta encontrada aquí .
  6. Inicializando la aplicación dos veces con ng-app y con bootstrap. Combatiendo AngularJS ejecutando el controlador dos veces
  7. Cuando se utiliza $ compile en todo el elemento en la función 'link' de la directiva que también tiene su propio controlador definido y utiliza devoluciones de llamada de este controlador en la plantilla a través de ng-click, etc. Aquí se encuentra la respuesta .
JesseBuesking
fuente
5. si está en Rails, debe eliminar la turbolinksgema de su application.jsarchivo. Eso me volvió loco, desperdicié todo el día para descubrir eso. Dolor real. Respuesta encontrada aquí
18agosto
6. Inicializando la aplicación dos veces con ng-app y con bootstrap. stackoverflow.com/questions/15535336/…
thadeuszlay
1
Gracias . Para mí, lo que funcionó fue poner una barra al final de la ruta
Pramod Sharma
Un millón de gracias, me estaba rompiendo la cabeza por esto. terminó siendo el número 7! (siempre el último ...).
Rabino Shuki Gur
Wow, lo creas o no, revisé todo en tu lista, y todavía tengo el problema.
Jacob Stamm el
14

Solo quiero agregar un caso más cuando el controlador puede iniciar dos veces (esto es real para angular.js 1.3.1):

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

En este caso, $ route.current ya estará configurado cuando ng-view se iniciará. Eso causa una doble inicialización.

Para solucionarlo, simplemente cambie ng-if a ng-show / ng-hide y todo funcionará bien.

DontRelaX
fuente
Me ayudó, estaba usando ng-show / ng-hide en lugar de ng-if, tenía dos ng-view, no es un problema, pero ng-if está desactivado, mientras que ng-show / ng-hide no
Dimitri Kopriwa
Gracias una tonelada. Este enfoque resolvió mi problema. Estaba llamando lo mismo ui-viewdos veces, lo que llamó al controlador dos veces.
curlyreggie
7

Me gustaría agregar como referencia:

La ejecución del código del controlador doble también puede ser causada al hacer referencia al controlador en una directiva que también se ejecuta en la página.

p.ej

return {

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

Cuando también tienes ng-controller = "myController" en tu HTML

gb2d
fuente
¿Cuál es la solución para esto?
Sagar Bhosale
1
Use solo uno u otro.
gb2d
6

Rompí mi aplicación y todas sus dependencias en bits sobre este problema (detalles aquí: la aplicación AngularJS se inicia dos veces (probé las soluciones habituales ...) )

Y al final, todo fue culpa del complemento Batarang Chrome.

Resolución en esta respuesta :

Recomiendo encarecidamente que lo primero en la lista de todos es deshabilitarlo por publicación antes de modificar el código.

Luis
fuente
Este fue mi problema. El complemento ejecuta una doble instancia de su aplicación Angular para que pueda proporcionar información de alcance (no sé por qué no puede usar la instancia inicial).
rgdigital
Me alegro de que haya ayudado, me llevó mucho tiempo resolver esto.
Lewis
5

Si sabe que su controlador se está ejecutando involuntariamente más de una vez, intente buscar en sus archivos el nombre del controlador infractor, por ejemplo: search: MyController en todos los archivos. Probablemente se copió en algún otro archivo html / js y olvidó cambiarlo cuando desarrolló o usó esos parciales / controladores. Fuente: cometí este error

usuario3901016
fuente
5

Tuve el mismo problema, en una aplicación simple (sin enrutamiento y una simple referencia de controlador ng) y el constructor de mi controlador se ejecutó dos veces. Finalmente, descubrí que mi problema era la siguiente declaración para iniciar automáticamente mi aplicación AngularJS en mi vista Razor

<html ng-app="mTest1">

También lo he arrancado manualmente usando angular.bootstrap, es decir

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

eliminar uno de ellos funcionó para mí.

athina.bikaki
fuente
4

En algunos casos, su directiva se ejecuta dos veces cuando simplemente no corrige cerca de su directiva de esta manera:

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

Esto ejecutará su directiva dos veces. También hay otro caso frecuente cuando su directiva se ejecuta dos veces:

¡asegúrese de no incluir su directiva en su index.html TWICE !

pleerock
fuente
Además, si está utilizando UI-Router, especificar el nombre de la directiva para la vista ui dentro de una clase parece resultar en una doble ejecución. Cambiarlo a E o <ui-view> </ui-view> corrige el problema de doble ejecución.
SteveGSD
2

Me he estado rascando la cabeza sobre este problema con AngularJS 1.4 rc build, luego me di cuenta de que ninguna de las respuestas anteriores era aplicable ya que se originó en la nueva biblioteca de enrutadores para Angular 1.4 y Angular 2 en el momento de escribir esto. Por lo tanto, estoy enviando una nota aquí para cualquiera que pueda estar usando la nueva biblioteca de rutas Angular.

Básicamente, si una página html contiene una ng-viewportdirectiva para cargar partes de su aplicación, al hacer clic en un hipervínculo especificado ng-link, el controlador de destino del componente asociado se cargará dos veces. La sutil diferencia es que, si el navegador ya ha cargado el controlador de destino, al volver a hacer clic en el mismo hipervínculo solo invocaría el controlador una vez.

Todavía no he encontrado una solución viable, aunque creo que este comportamiento es consistente con la observación planteada por shaunxu , y espero que este problema se resuelva en la futura construcción de una nueva biblioteca de rutas y junto con las versiones de AngularJS 1.4.

codeful.element
fuente
2

En mi caso, encontré dos vistas usando el mismo controlador.

$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
fuente
2

El problema con el que me encuentro puede ser tangencial, pero dado que googlear me trajo a esta pregunta, esto podría ser apropiado. El problema me asombra cuando uso el UI Router, pero solo cuando intento actualizar la página con el botón de actualización del navegador. La aplicación usa UI Router con un estado abstracto principal, y luego el secundario declara fuera del principal. En la run()función de la aplicación , hay un $state.go('...child-state...')comando. El estado primario usa a resolve, y al principio pensé que quizás un controlador secundario se está ejecutando dos veces.

Todo está bien antes de que la URL haya agregado el hash.
www.someoldwebaddress.org

Luego, una vez que la URL ha sido modificada por UI Router,
www.someoldwebaddress.org#/childstate

... y luego cuando actualizo la página con el botón de actualización del navegador , se $stateChangeStartactiva dos veces y cada vez apunta a childstate.

El resolveestado primario es el que se dispara dos veces.

Quizás esto sea un error; independientemente, esto parece eliminar el problema para mí: en el área de código donde $stateProviderse invoca por primera vez , primero verifique si window.location.hash es una cadena vacía. Si es así, todo está bien; si no es así, configure window.location.hash en una cadena vacía. Entonces parece que el $stateúnico intenta ir a algún lado una vez en lugar de dos.

Además, si no desea confiar en el valor predeterminado de la aplicación runy state.go(...), puede intentar capturar el valor hash y usar el valor hash para determinar el estado secundario en el que se encontraba justo antes de actualizar la página, y agregar una condición al área en su código donde configuró el state.go(...).

mg1075
fuente
1

En mi caso fue por el patrón de URL que usé

mi url era como / ui / project /: parameter1 /: parameter2.

No necesitaba paramerter2 en todos los casos de cambio de estado. En los casos en que no necesitaba el segundo parámetro, mi URL sería como / ui / project /: parameter1 /. Y así, cada vez que tuviera un cambio de estado, tendré mi controlador actualizado dos veces.

La solución fue establecer el parámetro2 como una cadena vacía y hacer el cambio de estado.

Sherin Syriac
fuente
1

He tenido esta doble inicialización por una razón diferente. Para algunas transiciones de ruta en mi aplicación, quería forzar el desplazamiento cerca de la parte superior de la página (por ejemplo, en resultados de búsqueda paginados ... al hacer clic en Siguiente debería ir a la parte superior de la página 2).

Hice esto agregando un oyente al $rootScope $on $viewContentLoadedque (basado en ciertas condiciones) ejecutado

$location.hash('top');

Sin darse cuenta, esto estaba causando que mis rutas fueran reevaluadas y que los controladores se reiniciaran.

Harry lima
fuente
1

En mi caso, cambiar el nombre del controlador a un nombre diferente resolvió el problema.

Hubo un conflicto de nombres de controladores con el módulo " angular-ui-tree ": cambié el nombre de mi controlador de "CatalogerTreeController" a " TreeController " y luego este controlador comienza a iniciarse dos veces en la página donde se usó la directiva " ui-tree " porque Esta directiva utiliza un controlador llamado " TreeController ".

AlexDEV.pro
fuente
1

Para aquellos que usan la sintaxis ControllerAs, simplemente declare la etiqueta del controlador en el $ routeprovider de la siguiente manera:

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

o

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

Después de declarar el $ routeprovider, no suministre el controlador como en la vista. En su lugar, use la etiqueta en la vista.

M. Arnold
fuente
0

Tuve el mismo problema y, después de probar todas las respuestas, finalmente descubrí que tenía una directiva que, en mi opinión, estaba vinculada al mismo controlador.

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

Después de eliminar la directiva, el controlador dejó de ser llamado dos veces. Ahora mi pregunta es, ¿cómo puede usar esta directiva vinculada al alcance del controlador sin este problema?

Daniel Vilas-Boas
fuente
0

Acabo de resolver el mío, que en realidad fue bastante decepcionante. Es una aplicación híbrida iónica, he usado ui-router v0.2.13. En mi caso, hay un lector epub (que usa epub.js) que informaba continuamente "no se encontró ningún documento" una vez que navego a mi biblioteca de libros y selecciono cualquier otro libro. Cuando volví a cargar, el libro del navegador se mostraba perfectamente, pero cuando seleccioné otro libro volví a tener el mismo problema.

Mi resolución fue muy simple. Me acabo de quitar reload:truede $state.go("app.reader", { fileName: fn, reload: true });miLibraryController

maksbd19
fuente
0

Tengo el mismo problema [email protected], y es porque la barra adicional al final de la ruta regex:

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

a

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

Y funciona correctamente.

B.Ma
fuente
0

Solo agrego mi caso aquí también:

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

Una vez añadí encodeURIComponent al parámetro, el problema se ha ido: $state.go('new_state', {foo: encodeURIComponent("foo@bar")}).

¿Que pasó? El carácter "@" en el valor del parámetro no está permitido en las URL. Como consecuencia, angular-ui-enrutador creó mi controlador dos veces: durante la primera creación pasó el "foo @ bar" original, durante la segunda creación pasó la versión codificada "foo% 40bar". Una vez que codifiqué explícitamente el parámetro como se muestra arriba, el problema desapareció.

Tom
fuente
-1

Me di cuenta de que el mío se llama dos veces porque estaba llamando al método dos veces desde mi 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 sección resaltada estaba provocando que findX () se llamara dos veces. Espero que ayude a alguien.

Nandu Prajapati
fuente