Zwalczanie AngularJS wykonującego kontroler dwa razy

532

Rozumiem, że AngularJS dwukrotnie przepuszcza jakiś kod, czasem nawet więcej, np. $watchZdarzenia, stale sprawdza stany modelu itp.

Jednak mój kod:

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

//...

Jest wykonywany dwukrotnie, wstawiając 2 rekordy do mojej bazy danych. Najwyraźniej wciąż się uczę, ponieważ od lat uderzam w to głową!

Greg
źródło
104
Jeśli kontroler działa dwa razy, sprawdź, czy nie inicjujesz aplikacji Angular dwa razy (automatycznie inicjując ją ng-appręcznie i ręcznie). Sprawdź także, czy podłączyłeś kontroler do wielu elementów (za pomocą ng-kontrolera).
Stewie,
7
czy możesz wyjaśnić, co rozumiesz przez „i za ​​pomocą ręcznego ładowania”?
sport
2
Zauważyłem zduplikowane zachowanie kontrolera w aplikacji odziedziczonej podczas rozwiązywania problemu z rejestrowaniem i podwójnego uruchamiania dzienników konsoli. Pierwszy kominek miał wartość, ale drugi był niezdefiniowany. Po usunięciu dla kontrolera dyrektywy HTML ng-kontroler drugi niezdefiniowany log dziennika konsoli zniknął.
Daniel Nalbach,
2
jeśli plik angular.js zostanie dodany dwukrotnie, to również może się zdarzyć
Mohit

Odpowiedzi:

1050

Router aplikacji określił nawigację MyControllertak:

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

Ale miałem to również w home.html:

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

To dwukrotnie przetrawiło kontroler. Usunięcie data-ng-controlleratrybutu z HTML rozwiązało problem. Alternatywnie controller:właściwość mogła zostać usunięta z dyrektywy routingu.

Ten problem pojawia się również podczas korzystania z nawigacji w kartach. Na przykład app.jsmoże zawierać:

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

Odpowiednia karta HTML raportów może przypominać:

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

Spowoduje to również dwukrotne uruchomienie kontrolera.

Greg
źródło
4
Wydaje mi się, że to najlepsze miejsce, aby po tym opuścić, po wielu godzinach przeczesywania mojego kodu, nie pytaj mnie, jak to zrobić, ale kiedy przejdę <div ng-view>...na <div class="ng-view">...ten problem, problem się zatrzymał. Nie spotkałem się wcześniej z takim zachowaniem, ale może ktoś inny też to ma.
Phix,
5
Istnieje dobre wytłumaczenie tego w dokumentacji dla ngController, docs.quarejs.org/api/ng/directive/ngController
Charles
1
Czy są jakieś sposoby zadeklarowania kontrolera na trasie, a następnie innego kontrolera na samej stronie HTML? Jak zapewnić zarówno działanie, jak i brak konfliktów? Czy to jest niepotrzebne?
Myśliciel
1
Prawdopodobnie nie jest to konieczne, może zamiast tego zastosować kilka dyrektyw?
Greg,
2
@Josh pozostawienie go w kodzie HTML zmniejsza elastyczność. Wiążesz swój szablon bezpośrednio z jednym kontrolerem. Możesz użyć ctrl jako składni z routingiem.
Greg,
127

Dokumentacja AngularJS - ngController
Pamiętaj, że możesz również dołączyć kontrolery do DOM, deklarując go w definicji trasy za pośrednictwem usługi $ route. Częstym błędem jest ponowne zadeklarowanie kontrolera za pomocą ng-kontrolera w samym szablonie. Spowoduje to, że kontroler zostanie podłączony i wykonany dwukrotnie.

Kiedy używasz ngRoute z ng-viewdyrektywą, kontroler domyślnie dołącza się do tego elementu dom (lub widoku interfejsu użytkownika, jeśli korzystasz z interfejsu routera). Dlatego nie będzie konieczne dołączanie go ponownie w szablonie.

shxfee
źródło
Duplikowana odpowiedź. Autor już odpowiedział, mówiąc to samo. Po prostu przewiń w dół, aż do końca strony.
Iago,
4
Uznałem za mylące, że autor umieścił go na marginesie, podczas gdy jest to oczywiście właściwy sposób na zrobienie tego. Cytat z dokumentów wyraźnie odpowiada na pytanie. I jestem pewien, że wielu uznałoby link do dokumentu za pomocny.
shxfee
To rozwiązało mój problem polegający na dwukrotnym wykonaniu kontrolera. Wszystko, co zrobiłem, to usunięcie kontrolera ng z szablonu, a teraz jest on wykonywany tylko raz.
torbenrudgaard
70

Właśnie to przeszedłem, ale problem był inny niż zaakceptowana odpowiedź. Naprawdę zostawiam to tutaj dla mojego przyszłego siebie, aby uwzględnić kroki, które wykonałem, aby to naprawić.

  1. Usuń nadmiarowe deklaracje kontrolera
  2. Sprawdź końcowe ukośniki na trasach
  3. Sprawdź ng-ifs
  4. Sprawdź, czy nie ma niepotrzebnych ng-viewpołączeń owijania (przypadkowo zostawiłem w ng-viewowijaniu moich faktycznych ng-view. Spowodowało to trzy połączenia do moich kontrolerów).
  5. Jeśli korzystasz z Railsów, powinieneś usunąć turbolinksklejnot ze swojego application.jspliku. Zmarnowałem cały dzień, żeby to odkryć. Znalazłem odpowiedź tutaj .
  6. Inicjowanie aplikacji dwukrotnie za pomocą ng-app i bootstrap. Zwalczanie AngularJS wykonującego kontroler dwa razy
  7. Podczas korzystania z kompilacji $ na całym elemencie w funkcji „link” dyrektywy, która również ma zdefiniowany własny kontroler i wykorzystuje wywołania zwrotne tego kontrolera w szablonie za pomocą kliknięcia ng itp. Znaleźć odpowiedź tutaj .
JesseBuesking
źródło
5. jeśli korzystasz z Railsów, powinieneś usunąć turbolinksklejnot ze swojego application.jspliku. To doprowadzało mnie do szału, zmarnowałem cały dzień, żeby to odkryć. Prawdziwy ból. Znaleziono odpowiedź tutaj
sierpnia
6. Dwukrotne zainicjowanie aplikacji za pomocą ng-app i bootstrap. stackoverflow.com/questions/15535336/…
thadeuszlay
1
Dzięki . Dla mnie rzeczą, która zadziałała, było cięcie na końcu trasy
Pramod Sharma
Dzięki milionowi, łamałem sobie głowę nad tym. skończyło się na 7 miejscu! (zawsze ostatni ...).
Rabin Shuki Gur,
Wow, wierz lub nie, przejrzałem wszystko na twojej liście i wciąż mam problem.
Jacob Stamm
14

Po prostu chcę dodać jeszcze jeden przypadek, gdy kontroler może zainicjować dwa razy (dotyczy to angular.js 1.3.1):

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

W tym przypadku $ route.current zostanie już ustawiony, gdy uruchomi się ng-view. To powoduje podwójną inicjalizację.

Aby to naprawić, po prostu zmień ng-if na ng-show / ng-hide, a wszystko będzie działać dobrze.

DontRelaX
źródło
Pomogło mi to, używałem ng-show / ng-hide zamiast ng-if, miałem dwa ng-view, to nie jest problem, ale ng-if wyłączone, podczas gdy ng-show / ng-hide nie
Dimitri Kopriwa
Wielkie dzięki. To podejście rozwiązało mój problem. Dzwonił tak samo ui-viewdwa razy, co dwukrotnie wywoływało kontroler.
curlyreggie,
7

Chciałbym dodać w celach informacyjnych:

Podwójne wykonanie kodu kontrolera może być również spowodowane odwołaniem się do kontrolera w dyrektywie, która również działa na stronie.

na przykład

return {

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

Gdy masz również ng-controller = "myController" w swoim HTML

gb2d
źródło
jakie jest na to rozwiązanie?
Sagar Bhosale
1
Używaj tylko jednego lub drugiego.
gb2d
7

Podczas korzystania z routera angular -ui-router z Angular 1.3+ wystąpił problem z dwukrotnym renderowaniem widoków podczas zmiany trasy . Spowodowało to również dwukrotne uruchomienie kontrolerów. Żadne z proponowanych rozwiązań nie działało dla mnie.

Jednak aktualizacja angular-ui-routerz 0.2.11 do 0.2.13 rozwiązała problem.

fracz
źródło
6

Rozerwałem moją aplikację i wszystkie jej zależności na strzępy w związku z tym problemem (szczegóły tutaj: aplikacja AngularJS inicjująca się dwukrotnie (wypróbowałem zwykłe rozwiązania ..) )

I w końcu to wszystko wina wtyczki Batarang Chrome.

Rozwiązanie w tej odpowiedzi :

Zdecydowanie polecam pierwszą rzeczą na liście dowolnej osoby jest wyłączenie jej dla każdego posta przed zmianą kodu.

Chwytak
źródło
To był mój problem. Wtyczka uruchamia podwójne wystąpienie aplikacji Angular, dzięki czemu może dostarczać informacje o zakresie (nie wiem, dlaczego nie może po prostu użyć początkowego wystąpienia).
rgdigital
Cieszę się, że to pomogło, zajęło mi to dużo czasu, aby to zrozumieć.
Lewis
5

Jeśli wiesz, że kontroler nieumyślnie wykonuje więcej niż jeden raz, spróbuj wyszukać w swoich plikach nazwę kontrolera, który go popełnił, np .: szukaj: MyController przez wszystkie pliki. Prawdopodobnie został wklejony do innego pliku HTML / JS i zapomniałeś go zmienić, kiedy zacząłeś programować lub używać tych częściowych kontrolerów. Źródło: Popełniłem ten błąd

użytkownik3901016
źródło
5

Miałem ten sam problem w prostej aplikacji (bez routingu i prostej referencji kontrolera ng), a konstruktor mojego kontrolera działał dwukrotnie. Wreszcie dowiedziałem się, że moim problemem była następująca deklaracja automatycznego ładowania aplikacji AngularJS w widoku Razor

<html ng-app="mTest1">

Uruchomiłem go również ręcznie za pomocą angular.bootstrap tj

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

więc usunięcie jednego z nich zadziałało dla mnie.

athina.bikaki
źródło
4

W niektórych przypadkach twoja dyrektywa działa dwa razy, kiedy po prostu nie poprawiasz zamknięcia dyrektywy w następujący sposób:

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

Spowoduje to dwukrotne uruchomienie dyrektywy. Innym często zdarza się, że dyrektywa działa dwukrotnie:

upewnij się, że nie dołączasz swojej dyrektywy do index.html DWUKROTNIE !

pleerock
źródło
Także, jeśli używasz interfejsu użytkownika routera, określenie nazwy dyrektywy dla widoku interfejsu użytkownika wewnątrz klasy wydaje się skutkować podwójnym wykonaniem. Zmiana na E lub <ui-view> </ui-view> rozwiązuje problem podwójnego wykonania.
SteveGSD,
2

Drapałem się po tym problemie z kompilacją AngularJS 1.4 rc, a potem zdałem sobie sprawę, że żadna z powyższych odpowiedzi nie miała zastosowania, ponieważ pochodzi ona z nowej biblioteki routerów dla Angular 1.4 i Angular 2 w momencie pisania tego tekstu. Dlatego upuszczam tutaj notatkę dla każdego, kto może korzystać z nowej biblioteki tras Angular.

Zasadniczo, jeśli strona HTML zawiera ng-viewportdyrektywę dotyczącą ładowania części aplikacji, kliknięcie hiperłącza podanego za pomocą ng-linkspowoduje, że docelowy kontroler powiązanego komponentu zostanie załadowany dwukrotnie. Subtelna różnica polega na tym, że jeśli przeglądarka załadowała już kontroler docelowy, ponowne kliknięcie tego samego hiperłącza wywołałoby kontroler tylko raz.

Nie znalazłem jeszcze realnego obejścia, chociaż uważam, że takie zachowanie jest zgodne z obserwacją podniesioną przez shaunxu i mam nadzieję, że problem ten zostanie rozwiązany w przyszłej wersji nowej biblioteki tras i wraz z wydaniami AngularJS 1.4.

codeful.element
źródło
2

W moim przypadku znalazłem dwa widoki przy użyciu tego samego kontrolera.

$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
źródło
2

Problem, który napotykam, może być styczny, ale skoro Google przyniósł mi to pytanie, może to być właściwe. Problem pojawia się w mojej brzydkiej głowie podczas korzystania z routera interfejsu użytkownika, ale tylko wtedy, gdy próbuję odświeżyć stronę za pomocą przycisku odświeżania przeglądarki. Aplikacja używa routera interfejsu użytkownika z nadrzędnym stanem abstrakcyjnym, a następnie podrzędnym wyłącza nadrzędny. W run()funkcji aplikacji jest $state.go('...child-state...')polecenie. Stan nadrzędny używaresolve i na początku myślałem, że może kontroler potomny wykonuje dwa razy.

Wszystko jest w porządku, zanim do adresu URL zostanie dodany skrót.
www.someoldwebaddress.org

Następnie po zmodyfikowaniu adresu URL przez router interfejsu użytkownika
www.someoldwebaddress.org#/childstate

... a następnie, gdy odświeżam stronę za pomocą przycisku odświeżania przeglądarki , $stateChangeStarturuchamia się dwukrotnie i za każdym razem wskazuje childstate.

Stan resolvenadrzędny jest uruchamiany dwukrotnie.

Być może jest to kludge; niezależnie od tego, wydaje mi się, że to eliminuje problem: w obszarze kodu, który $stateProviderjest wywoływany po raz pierwszy , najpierw sprawdź, czy window.location.hash jest pustym ciągiem. Jeśli tak, wszystko jest dobrze; jeśli tak nie jest, ustaw window.location.hash na pusty ciąg. Wydaje się, że $statejedyną próbą jest udać się gdzieś raz, a nie dwa razy.

Ponadto, jeśli nie chcesz polegać na niewypłacalności aplikacji runi state.go(...)można próbować uchwycić wartość skrótu i użyć wartości hash do określenia stanu dziecka były na krótko przed odświeżania strony i dodać warunek do obszaru w swojej kod, w którym ustawiłeś state.go(...).

mg1075
źródło
1

W moim przypadku było to spowodowane użyciem wzorca adresu URL

mój adres URL był jak / ui / project /: parameter1 /: parameter2.

Nie potrzebowałem paramerter2 we wszystkich przypadkach zmiany stanu. W przypadkach, gdy nie potrzebowałem drugiego parametru, mój adres URL byłby jak / ui / project /: parameter1 /. I tak za każdym razem, gdy miałem zmianę stanu, mój kontroler będzie odświeżany dwukrotnie.

Rozwiązaniem było ustawienie parametru 2 jako pustego ciągu i dokonanie zmiany stanu.

Sherin Syriac
źródło
1

Ta podwójna inicjalizacja nastąpiła z innego powodu. W przypadku niektórych przejść tras w mojej aplikacji chciałem wymusić przewijanie do góry strony (np. W paginowanych wynikach wyszukiwania ... kliknięcie przycisku następnego spowoduje przejście do góry strony 2).

Zrobiłem to, dodając do nas program nasłuchujący $rootScope $on $viewContentLoaded(na podstawie określonych warunków)

$location.hash('top');

Nieumyślnie spowodowało to ponowne oszacowanie moich tras i ponowne zainicjowanie kontrolerów

Harry Lime
źródło
1

W moim przypadku zmiana nazwy kontrolera na inną nazwę rozwiązała problem.

Wystąpił konflikt nazw kontrolerów z modułem „ angular -ui-tree ”: zmieniłem nazwę mojego kontrolera z „CatalogerTreeController” na „ TreeController ”, a następnie ten kontroler zaczyna być inicjowany dwukrotnie na stronie, na której użyto dyrektywy „ ui-tree ”, ponieważ ta dyrektywa używa kontrolera o nazwie „ TreeController ”.

AlexDEV.pro
źródło
1

Dla tych, którzy używają składni ControllerAs, po prostu zadeklaruj etykietę kontrolera w $ routeprovider w następujący sposób:

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

lub

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

Po zadeklarowaniu $ routeprovider, nie podawaj kontrolera jak w widoku. Zamiast tego użyj etykiety w widoku.

M. Arnold
źródło
0

Miałem ten sam problem i po wypróbowaniu wszystkich odpowiedzi w końcu odkryłem, że według mnie mam dyrektywę związaną z tym samym kontrolerem.

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

Po usunięciu dyrektywy kontroler przestał być wywoływany dwukrotnie. Teraz moje pytanie brzmi: w jaki sposób można wykorzystać tę dyrektywę związaną z zakresem kontrolera bez tego problemu?

Daniel Vilas-Boas
źródło
0

Właśnie rozwiązałem mój, co było dość rozczarowujące. To jonowa aplikacja hybrydowa, użyłem interfejsu użytkownika routera v0.2.13. W moim przypadku istnieje czytnik epubów (korzystający z epub.js), który stale zgłaszał komunikat „nie znaleziono dokumentu” po przejściu do biblioteki książek i wybraniu innej książki. Po ponownym załadowaniu książka przeglądarki była renderowana idealnie, ale kiedy wybrałem inną książkę, znowu ten sam problem.

Moje rozwiązanie było bardzo proste. Właśnie usunąłem reload:truez $state.go("app.reader", { fileName: fn, reload: true });mojegoLibraryController

maksbd19
źródło
0

Mam ten sam problem [email protected], a to dlatego, że dodatkowy ukośnik na końcu trasy wyrażenia regularnego:

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

do

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

i działa poprawnie.

B.Ma
źródło
0

Dodając tutaj również moją skrzynkę:

Ja pomocą skośnych ui routerem z$state.go('new_state', {foo: "foo@bar"})

Kiedyś dodałem encodeURIComponent do parametru problem zniknął:$state.go('new_state', {foo: encodeURIComponent("foo@bar")}) .

Co się stało? Znak „@” w wartości parametru jest niedozwolony w adresach URL. W związku z tym router kątowy ui-router utworzył mój kontroler dwukrotnie: podczas pierwszego tworzenia przekazał oryginalny „foo @ bar”, podczas drugiego tworzenia przekazał zakodowaną wersję „foo% 40bar”. Po jawnym zakodowaniu parametru, jak pokazano powyżej, problem zniknął.

Tomek
źródło
-1

Zrozumiałem, że mój jest wywoływany dwa razy, ponieważ dzwoniłem do tej metody dwa razy z mojego 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>`

Podświetlona sekcja spowodowała dwukrotne wywołanie findX (). Mam nadzieję, że to komuś pomoże.

Nandu Prajapati
źródło