Combatendo o AngularJS executando o controlador duas vezes

532

Entendo que o AngularJS executa algum código duas vezes, às vezes até mais, como $watcheventos, verificando constantemente os estados do modelo etc.

No entanto meu 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);
});

//...

É executado duas vezes, inserindo 2 registros no meu banco de dados. Eu claramente ainda estou aprendendo, já que estou batendo minha cabeça contra isso há séculos!

Greg
fonte
104
Se o seu controlador estiver executando duas vezes, verifique se você não está inicializando o aplicativo Angular duas vezes (inicializando-o automaticamente com ng-appe com autoinicialização). Verifique também se você conectou seu controlador a vários elementos (com ng-controller).
Stewie
7
você pode explicar o que você quer dizer com "e com inicialização manual"?
esportes
2
Percebi o comportamento duplicado do controlador em um aplicativo que herdei ao solucionar um problema com o log e ver os logs do console dispararem duas vezes. O primeiro registro de log teve um valor, mas o segundo não foi definido. Depois de remover a diretiva ng-controller HTML do controlador, a segunda inicialização de log do console indefinida desapareceu.
Daniel Nalbach
2
se angularjs está sendo adicionado duas vezes, em seguida, também isso pode acontecer
Mohit

Respostas:

1050

O roteador de aplicativos especificou a navegação da seguinte MyControllermaneira:

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

Mas eu também tive isso em home.html:

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

Isso digeriu o controlador duas vezes. A remoção do data-ng-controlleratributo do HTML resolveu o problema. Como alternativa, a controller:propriedade poderia ter sido removida da diretiva de roteamento.

Esse problema também aparece ao usar a navegação com guias. Por exemplo, app.jspode conter:

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

O HTML correspondente da guia de relatórios pode se parecer com:

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

Isso também resultará na execução do controlador duas vezes.

Greg
fonte
4
Este parece ser o melhor lugar para deixar isso, depois de muitas horas analisando meu código, não me pergunte como, mas quando mudo <div ng-view>...para <div class="ng-view">..., esse problema parou para mim. Eu nunca me deparei com esse comportamento antes, mas talvez alguém também tenha isso.
phix
5
Há uma boa explicação para isso nos docs para ngController, docs.angularjs.org/api/ng/directive/ngController
Charles
1
Existem maneiras de declarar um controlador na rota e outro controlador na própria página html? Como garantir os dois trabalhos e sem conflitos? Ou isso é desnecessário?
Thinkerer
1
Provavelmente é desnecessário, talvez use algumas diretivas?
Greg
2
@ Josh deixando-o no HTML reduz a flexibilidade. Você está vinculando seu modelo diretamente a um controlador. Você pode usar ctrl como sintaxe no roteamento.
Greg
127

AngularJS docs - ngController
Observe que você também pode anexar controladores ao DOM declarando-o em uma definição de rota através do serviço $ route. Um erro comum é declarar o controlador novamente usando o ng-controller no próprio modelo. Isso fará com que o controlador seja conectado e executado duas vezes.

Quando você usa ngRoute com a ng-viewdiretiva, o controlador é anexado a esse elemento dom por padrão (ou ui-view se você usar ui-router). Portanto, você não precisará anexá-lo novamente no modelo.

shxfee
fonte
Resposta duplicada. O autor já respondeu dizendo o mesmo. Basta rolar para baixo até o final da página.
Iago
4
Achei confuso o autor colocá-lo como uma nota lateral, enquanto é obviamente a maneira correta de fazê-lo. A citação dos documentos responde claramente à pergunta. E tenho certeza que muitos acham o link para o documento útil.
shxfee
Isso resolveu meu problema de o controlador ser executado duas vezes. Tudo o que fiz foi remover o ng-controller no modelo e agora está sendo executado apenas uma vez.
torbenrudgaard
70

Acabei de passar por isso, mas o problema era diferente da resposta aceita. Estou realmente deixando isso aqui para o meu eu futuro, para incluir as etapas pelas quais passei para corrigi-lo.

  1. Remover declarações redundantes do controlador
  2. Verificar barras finais nas rotas
  3. Verificar se há ng-ifs
  4. Verifique se há ng-viewchamadas de quebra de linha desnecessárias (deixei acidentalmente uma ng-viewque estava encerrando a minha atual ng-view. Isso resultou em três chamadas para meus controladores.)
  5. Se você estiver no Rails, remova a turbolinksgema do seu application.jsarquivo. Eu perdi um dia inteiro para descobrir isso. Encontre a resposta aqui .
  6. Inicializando o aplicativo duas vezes com ng-app e com bootstrap. Combatendo o AngularJS executando o controlador duas vezes
  7. Ao usar $ compile no elemento inteiro na função 'link' da diretiva que também possui seu próprio controlador definido e usa retornos de chamada desse controlador no modelo via ng-click etc. Encontre a resposta aqui .
JesseBuesking
fonte
5. Se você estiver no Rails, remova a turbolinksgema do seu application.jsarquivo. Isso me deixou louco, desperdiçado o dia inteiro para descobrir isso. Dor de verdade. A resposta encontrada aqui é
18
6. Inicializando o aplicativo duas vezes com ng-app e com autoinicialização. stackoverflow.com/questions/15535336/...
thadeuszlay
1
Obrigado . Para mim, o que funcionou foi colocar uma barra no final do percurso
Pramod Sharma
Graças um milhão, estava quebrando minha cabeça sobre isso. acabou sendo o número 7! (sempre o último ...).
Rabino Shuki Gur
Uau, acredite ou não, passei por tudo na sua lista e ainda estou tendo o problema.
Jacob Stamm
14

Só quero adicionar mais um caso quando o controlador puder iniciar duas vezes (isso é real para angular.js 1.3.1):

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

Nesse caso, $ route.current já estará definido quando o ng-view for iniciado. Isso causa dupla inicialização.

Para corrigi-lo, basta alterar ng-if para ng-show / ng-hide e tudo funcionará bem.

DontRelaX
fonte
Isso me ajudou, eu estava usando ng-show / ng-hide em vez de ng-if, eu tinha dois ng-view, não é um problema, mas ng-if é desativado enquanto ng-show / ng-hide não
Dimitri Kopriwa
Muito obrigado. Essa abordagem resolveu meu problema. Estava chamando o mesmo ui-viewduas vezes que chamou o controlador duas vezes.
simples
7

Gostaria de adicionar como referência:

A execução dupla do código do controlador também pode ser causada pela referência ao controlador em uma diretiva que também é executada na página.

por exemplo

return {

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

Quando você também possui ng-controller = "myController" no seu HTML

gb2d
fonte
Qual é a solução para isso?
precisa saber é o seguinte
1
Use apenas um ou outro.
precisa saber é
6

Rasguei meu aplicativo e todas as suas dependências em bits sobre esse problema (detalhes aqui: aplicativo AngularJS iniciando duas vezes (tentei as soluções usuais ..) )

E no final, tudo foi culpa do plugin Batarang Chrome.

Resolução nesta resposta :

Eu recomendo fortemente que a primeira coisa na lista de qualquer pessoa seja desativá-la pela postagem antes de alterar o código.

Lewis
fonte
Este foi o meu problema. O plug-in executa uma instância dupla do seu aplicativo Angular para fornecer informações sobre o escopo (não sei por que ele não pode apenas usar a instância inicial).
Rgdigital
Estou feliz que tenha ajudado, demorei muito tempo para descobrir isso.
Lewis
5

Se você souber que seu controlador está executando acidentalmente mais de uma vez, tente pesquisar nos arquivos o nome do controlador incorreto, por exemplo: search: MyController em todos os arquivos. Provavelmente ele foi copiado e colado em algum outro arquivo html / js e você esqueceu de alterá-lo quando começou a desenvolver ou usar esses parciais / controladores. Fonte: Eu cometi este erro

user3901016
fonte
5

Eu tive o mesmo problema, em um aplicativo simples (sem roteamento e uma referência ng-controller simples) e o construtor do meu controlador foi executado duas vezes. Por fim, descobri que meu problema era a seguinte declaração para autoinicializar automaticamente meu aplicativo AngularJS na visualização Razor

<html ng-app="mTest1">

Também o iniciei manualmente usando angular.bootstrap, ou seja,

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

então remover um deles funcionou para mim.

athina.bikaki
fonte
4

Em alguns casos, sua diretiva é executada duas vezes quando você simplesmente não corrige a diretiva de fechamento dessa maneira:

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

Isso executará sua diretiva duas vezes. Também há outro caso frequente em que sua diretiva é executada duas vezes:

verifique se você não está incluindo sua diretiva no seu index.html DUAS VEZES !

pleerock
fonte
Além disso, se você estiver usando o roteador da interface do usuário, especificar o nome da diretiva para a interface do usuário dentro de uma classe parece resultar em uma execução dupla. Mudar para E ou <ui-view> </ui-view> corrige o problema de dupla execução.
28915 SteveGSD
2

Estando ciscando a cabeça sobre esse problema com o AngularJS 1.4 rc build, percebi que nenhuma das respostas acima era aplicável, pois ela se originou da nova biblioteca de roteadores do Angular 1.4 e do Angular 2 no momento da redação deste documento. Portanto, deixo aqui uma nota para qualquer pessoa que possa estar usando a nova biblioteca de rotas Angular.

Basicamente, se uma página html contiver uma ng-viewportdiretiva para carregar partes do seu aplicativo, clicar em um hiperlink especificado em ng-linkfaria com que o controlador de destino do componente associado fosse carregado duas vezes. A diferença sutil é que, se o navegador já carregou o controlador de destino, clicar novamente no mesmo hiperlink chamaria o controlador apenas uma vez.

Ainda não encontrei uma solução viável, embora eu acredite que esse comportamento seja consistente com a observação levantada pelo shaunxu , e espero que esse problema seja resolvido na construção futura da nova biblioteca de rotas e nas versões do AngularJS 1.4.

codeful.element
fonte
2

No meu caso, encontrei duas visualizações usando o mesmo 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
fonte
2

O problema que estou encontrando pode ser tangencial, mas como o Google me levou a essa pergunta, isso pode ser apropriado. O problema é horrível para mim ao usar o roteador da interface do usuário, mas apenas quando tento atualizar a página com o botão de atualização do navegador. O aplicativo usa o roteador da interface do usuário com um estado abstrato pai e os estados filho fora do pai. Na run()função do aplicativo , há um $state.go('...child-state...')comando. O estado pai usa a resolvee, a princípio, pensei que talvez um controlador filho esteja executando duas vezes.

Tudo está bem antes que o URL tenha o hash anexado.
www.someoldwebaddress.org

Depois que o URL for modificado pelo roteador da interface do usuário,
www.someoldwebaddress.org#/childstate

... e, em seguida, quando atualizo a página com o botão de atualização do navegador , o $stateChangeStartacionador é acionado duas vezes e cada vez aponta para o childstate.

O resolveestado pai é o que está disparando duas vezes.

Talvez isso seja uma farsa; de qualquer maneira, isso parece eliminar o problema para mim: na área do código onde $stateProvideré chamado pela primeira vez , verifique primeiro se o window.location.hash é uma string vazia. Se for, tudo é bom; caso contrário, defina o window.location.hash para uma sequência vazia. Então parece que as $stateúnicas tentativas de ir a algum lugar uma vez e não duas.

Além disso, se você não deseja confiar no padrão do aplicativo rune state.go(...), pode tentar capturar o valor do hash e usá-lo para determinar o estado filho em que estava antes da atualização da página e adicionar uma condição à área em seu código onde você define o state.go(...).

mg1075
fonte
1

No meu caso, foi por causa do padrão de URL que usei

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

Eu não precisava do paramerter2 em todos os casos de mudança de estado. Nos casos em que não precisei do segundo parâmetro, meu URL seria como / ui / project /: parameter1 /. E assim, sempre que eu tinha uma alteração de estado, meu controlador era atualizado duas vezes.

A solução foi definir o parâmetro2 como uma sequência vazia e fazer a mudança de estado.

Sherin Syriac
fonte
1

Eu tive essa inicialização dupla por um motivo diferente. Para algumas transições de rota no meu aplicativo, eu queria forçar a rolagem até o topo da página (por exemplo, nos resultados de pesquisa paginados ... clicar em Avançar deve levá-lo ao topo da página 2).

Fiz isso adicionando um ouvinte ao $rootScope $on $viewContentLoadedqual (com base em determinadas condições) executou

$location.hash('top');

Inadvertidamente, isso estava fazendo com que minhas rotas fossem reavaliadas e os controladores fossem reinicializados

Harry Lime
fonte
1

No meu caso, renomear o controlador com um nome diferente resolveu o problema.

Houve um conflito de nomes de controladores com o módulo " angular-ui-tree ": renomeei meu controlador de "CatalogerTreeController" para " TreeController " e esse controlador começa a ser iniciado duas vezes na página em que a diretiva " ui-tree " foi usada porque Esta diretiva usa o controlador chamado " TreeController ".

AlexDEV.pro
fonte
1

Para aqueles que usam a sintaxe ControllerAs, basta declarar o rótulo do controlador no $ routeprovider da seguinte maneira:

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

ou

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

Depois de declarar o $ routeprovider, não forneça o controlador como na exibição. Em vez disso, use o rótulo na exibição.

M. Arnold
fonte
0

Eu tive o mesmo problema e, depois de tentar todas as respostas, finalmente descobri que tinha uma diretiva na minha opinião que estava vinculada ao mesmo controlador.

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

Após remover a diretiva, o controlador parou de ser chamado duas vezes. Agora, minha pergunta é: como usar essa diretiva vinculada ao escopo do controlador sem esse problema?

Daniel Vilas-Boas
fonte
0

Acabei de resolver o meu, o que foi realmente muito decepcionante. É um aplicativo híbrido iônico, usei o ui-router v0.2.13. No meu caso, há um leitor de epub (usando epub.js) que relatava continuamente "nenhum documento encontrado" depois que navegava para a biblioteca de livros e selecionava qualquer outro livro. Quando recarreguei, o livro do navegador estava sendo renderizado perfeitamente, mas quando selecionei outro livro, o mesmo problema foi novamente.

Minha resolução foi muito simples. I acabou de remover reload:truea partir $state.go("app.reader", { fileName: fn, reload: true });da minhaLibraryController

maksbd19
fonte
0

Eu tenho o mesmo problema [email protected], e porque a barra extra no final da rota regex:

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

para

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

e funciona corretamente.

B.Ma
fonte
0

Apenas adicionando meu caso aqui também:

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

Uma vez eu adicionei encodeURIComponent ao parâmetro, o problema se foi: $state.go('new_state', {foo: encodeURIComponent("foo@bar")}).

O que aconteceu? O caractere "@" no valor do parâmetro não é permitido nos URLs. Como conseqüência, o angular-ui-router criou meu controlador duas vezes: durante a primeira criação, passou o "foo @ bar" original, durante a segunda criação passou a versão codificada "foo% 40bar". Depois de codificar explicitamente o parâmetro, como mostrado acima, o problema desapareceu.

Tom
fonte
-1

Eu descobri que o meu está sendo chamado duas vezes é porque eu estava chamando o método duas vezes no meu 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>`

A seção destacada estava fazendo com que findX () fosse chamado duas vezes. Espero que ajude alguém.

Nandu Prajapati
fonte