Боротьба з виконанням контролера AngularJS двічі


532

Я розумію, що AngularJS проходить через якийсь код двічі, іноді навіть більше, як $watchподії, постійно перевіряючи стан моделей тощо.

Однак мій код:

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

//...

Виконується двічі, вставляючи 2 записи в мій БД. Я чітко все ще вчуся, тому що я бовтав голову проти цього протягом століть!


104
Якщо ваш контролер працює два рази, переконайтеся, що ви не ініціалізуєте додаток Angular двічі (автоматично ініціалізуючи його ng-appз ручним завантажувачем). Також перевірте, чи приєднали ви свій контролер до декількох елементів (за допомогою ng-контролера).
Стюі

7
чи можете ви пояснити, що ви маєте на увазі під "та з ручним завантаженням"?
спорт


2
Я помітив повторювану поведінку контролера у програмі, яку я успадкував, коли вирішував проблему з веденням журналу та бачив, як консольні журнали двічі запускаються. Перший пожежогасіння мав значення, але другий був невизначений. Після вилучення директиви HTML ng-Controller для контролера, другий пожежонебезпечний журнал консолі, який не було визначено, згас.
Даніель Налбах

2
якщо додано angular.js двічі, то також це може статися
Mohit

Відповіді:


1050

Маршрутизатор додатка вказав навігацію MyControllerтак:

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

Але я також мав це у home.html:

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

Це засвоював контролер двічі. Видалення data-ng-controllerатрибуту з HTML вирішило проблему. Крім того, controller:властивість могло бути вилучено з директиви про маршрутизацію.

Ця проблема також з’являється при використанні навігації з вкладками. Наприклад, app.jsможе містити:

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

Відповідна вкладка HTML звітів може нагадувати:

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

Це також призведе до запуску контролера двічі.


4
Це здається найкращим місцем, щоб залишити це, після багатьох годин розчісування коду, не запитуйте мене як, але коли я переходжу <div ng-view>...на <div class="ng-view">...цю проблему, для мене це зупинилося. Я раніше не стикався з такою поведінкою, але, можливо, хтось інший теж має це.
Фікс

5
Є гарне пояснення цього в документах для ngController, docs.angularjs.org/api/ng/directive/ngController
Чарльз

1
Чи є способи оголосити контролер у маршруті, а потім інший контролер на самій html-сторінці? Як забезпечити як роботу, так і відсутність конфліктів? Або це зайве?
Мислитель

1
Це, мабуть, непотрібно, можливо, замість цього скористайтеся парою директив?
Грег

2
@Josh, залишаючи його в HTML, зменшує гнучкість. Ви прив'язуєте шаблон безпосередньо до одного контролера. Ви можете використовувати ctrl як синтаксис із маршрутизацією.
Грег

127

AngularJS docs - ngController
Зауважте, що ви також можете приєднати контролери до DOM, оголосивши його у визначенні маршруту через службу маршруту $. Поширена помилка - знову оголосити контролер, використовуючи ng-контролер у самому шаблоні. Це призведе до того, що контролер буде прикріплений та виконаний двічі.

Коли ви використовуєте ngRoute з ng-viewдирективою, контролер приєднується до цього елемента dom за замовчуванням (або u-view, якщо ви використовуєте ui-роутер). Тому вам не потрібно буде знову вкладати його в шаблон.


Дублюється анвесер. Автор уже відповів, сказавши те саме. Просто прокрутіть униз до кінця сторінки.
Іаго

4
Я вважав це заплутаним, що автор ставив це як бічну ноту, хоча це, очевидно, правильний спосіб. Цитата з документів чітко відповідає на питання. І я впевнений, що багато хто вважатиме посилання на документ корисним.
shxfee

Це вирішило мою проблему виконання контролера двічі. Все, що я зробив, - це видалити ng-контролер з шаблону, і тепер його виконується лише один раз.
torbenrudgaard

70

Я щойно пройшов через це, але питання відрізнялося від прийнятої відповіді. Я справді залишаю це для своєї майбутньої самості, щоб включити кроки, через які я пройшов, щоб виправити це.

  1. Видаліть зайві декларації контролера
  2. Перевірте кінці косої риски в маршрутах
  3. Перевірити для ng-ifs
  4. Перевірте, чи немає зайвих завершувальних ng-viewдзвінків (я випадково залишив у своєму, ng-viewщо загортав фактичне ng-view. Це призвело до трьох дзвінків до моїх контролерів.)
  5. Якщо ви знаходитесь на Rails, вам слід видалити turbolinksдорогоцінний камінь із вашого application.jsфайлу. Я витратив цілий день, щоб відкрити це. Знайдено відповідь тут .
  6. Ініціалізація програми двічі за допомогою ng-app та з завантажувальною програмою. Боротьба з виконанням контролера AngularJS двічі
  7. При використанні $ компілюється на весь елемент в «link'-функції директиви , яка також має свій власний контролер , певні і використовує зворотні виклики цього контролера в шаблон через нг клік і т.д. Знайдено відповідь тут .

5. якщо ви знаходитесь на Rails, вам слід видалити turbolinksдорогоцінний камінь зі свого application.jsфайлу. Це змусило мене збожеволіти, витрачало цілий день, щоб відкрити це. Справжній біль. Знайдено відповідь тут
18augst

6. Ініціалізація програми двічі за допомогою ng-app та з завантажувальним програмним забезпеченням. stackoverflow.com/questions/15535336 / ...
thadeuszlay

1
Дякую . Для мене те, що спрацювало, - це покласти косу рису в кінці маршруту
Pramod Sharma

Спасибі мільйон, зламав мені голову через це. виявився номером 7! (завжди останній ...).
Раббі Шукі Гур

Нічого собі, вірите чи ні, я все переглянув у вашому списку, і у мене все ще виникає проблема.
Якоб Стамм

14

Просто хочу додати ще один випадок, коли контролер може ініціювати двічі (це актуально для angular.js 1.3.1):

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

У цьому випадку $ route.current буде вже встановлено, коли ng-view буде ініційовано. Це спричиняє подвійну ініціалізацію.

Щоб виправити це, просто змініть ng-if на ng-show / ng-hid і все буде добре.


Це допомогло мені, я використовував ng-show / ng-hid замість ng-якщо у мене було два ng-view, це не проблема, але ng-якщо відключити, коли ng-show / ng-hid не робить
Dimitri Копріва

Дякую тонну. Цей підхід вирішив мою проблему. Викликав той самий ui-viewдвічі, який дзвонив контролеру двічі.
curlyreggie

7

Хочеться додати для довідки:

Подвійне виконання коду контролера також може бути викликане посиланням на контролер у директиві, яка також працює на сторінці.

напр

return {

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

Якщо у вашому HTML також є ng-controller = "myController"


яке рішення для цього?
Сагар Бхосале

1
Використовуйте лише те чи інше.
gb2d

7

Під час використання angular-ui-router з кутовим 1.3+ виникла проблема з рендерінгом переглядів двічі при переході маршруту . Це призвело до виконання контролерів двічі. Жодне із запропонованих рішень не працювало для мене.

Однак оновлення angular-ui-routerз 0,2.11 до 0,2.13 вирішило для мене проблему.


6

Я зірвав додаток і всі його залежності від бітів над цією проблемою (детальніше тут: додаток AngularJS ініціюється двічі (спробував звичайні рішення ..) )

І врешті-решт, у всьому винна плагін Batarang Chrome.

Резолюція у цій відповіді :

Я настійно рекомендую перше, що є у списку когось, - відключити його на посаді перед зміною коду.


Це було моє питання. Плагін виконує подвійний екземпляр програми Angular, щоб він міг надавати інформацію про область (не знаю, чому він не може просто використовувати початковий екземпляр).
rgdigital

Я радий, що це допомогло, мені знадобилося дуже довгий час, щоб зрозуміти це.
Льюїс

5

Якщо ви знаєте, що ваш контролер невмисно виконує не один раз, спробуйте здійснити пошук ваших файлів за ім'ям контролера-порушення, наприклад: пошук: MyController через усі файли. Ймовірно, він потрапив до копії у якийсь інший файл html / js, і ви забули змінити його, коли перейшли до розробки або використання цих партій / контролерів. Джерело: Я допустив цю помилку


5

У мене була така ж проблема в простому додатку (без маршрутизації та простої посилання на ng-контролер), і конструктор мого контролера запускався двічі. Нарешті, я з’ясував, що моєю проблемою було таке декларація щодо автоматичного завантаження програми AngularJS в моєму представленні Razor

<html ng-app="mTest1">

Я також завантажував його вручну за допомогою angular.bootstrap, тобто

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

тому видалення одного з них, це спрацювало на мене.


4

У деяких випадках ваша директива працює двічі, коли ви просто не виправляєте закриття директиви так:

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

Це запустить вашу директиву двічі. Також є ще один часто випадок, коли ваша директива працює двічі:

переконайтеся, що ви не включаєте свою директиву у свої index.html ДВАХ !


Крім того, якщо ви використовуєте UI-маршрутизатор, вказівка ​​назви директиви для ui-view всередині класу, здається, призводить до подвійного виконання. Змінивши його на E або <ui-view> </ui-view>, вирішує проблему подвійного виконання.
SteveGSD

2

Я почав чухати голову над цією проблемою з збіркою AngularJS 1.4 rc, тоді зрозумів, що жоден із наведених вище відповідей не застосований, оскільки він був створений з нової бібліотеки маршрутизаторів для Angular 1.4 та Angular 2 під час написання цього запису. Тому я кидаю тут замітку для всіх, хто, можливо, використовує нову бібліотеку маршрутів Angular.

В основному, якщо на html-сторінці міститься ng-viewportдиректива щодо завантаження частин вашої програми, натискання на гіперпосилання, вказане в, ng-linkпризведе до завантаження цільового контролера пов'язаного компонента двічі. Тонка різниця полягає в тому, що якщо браузер уже завантажив цільовий контролер, повторне натискання на ту саму гіперпосилання викличе контролер лише один раз.

Ще не знайшли життєздатного вирішення, хоча я вважаю, що така поведінка відповідає спостереженню, зробленому shaunxu , і, сподіваємось, це питання буде вирішене в майбутньому складання нової бібліотеки маршрутів і разом з випусками AngularJS 1.4.


2

У моєму випадку я знайшов два подання за допомогою одного і того ж контролера.

$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

Проблема, з якою я стикаюсь, може бути дотичною, але оскільки Google потягнув мене до цього питання, це може бути доречним. Проблема отримує свою некрасиву голову для мене при використанні маршрутизатора UI, але лише тоді, коли я намагаюся оновити сторінку кнопкою оновлення браузера. У додатку використовується маршрутизатор користувальницького інтерфейсу з абстрактним станом для батьків, а потім дочірній стан відключається від батьківського. На функції програми run()є $state.go('...child-state...')команда. Батьківський стан використовує a resolve, і спочатку я подумав, що, можливо, дочірній контролер виконується двічі.

Все добре, перш ніж URL-адресу було додано хеш.
www.someoldwebaddress.org

Потім, коли URL-адресу було змінено маршрутизатором інтерфейсу,
www.someoldwebaddress.org#/childstate

... а потім, коли я оновлюю сторінку кнопкою оновлення браузера , $stateChangeStartзапускається двічі, і кожен раз вказує на childstate.

resolveНа батьківському стані є те , що в два рази стріляли.

Можливо, це хитрість; незалежно, це, як видається, усуває проблему для мене: у області коду, куди $stateProviderвперше викликається, спочатку перевірте, чи вікно вікна.location.hash порожнє. Якщо є, все добре; якщо його немає, то встановіть window.location.hash на порожній рядок. Тоді здається, що $stateєдині спроби кудись поїхати один раз, а не двічі.

Крім того, якщо ви не хочете покладатися на програму за замовчуванням runі state.go(...), ви можете спробувати зафіксувати хеш-значення та використовувати хеш-значення, щоб визначити стан дитини, на якому ви знаходилися безпосередньо перед оновленням сторінки, і додати умову до області у вашому код, де ви встановили state.go(...).


1

У моєму випадку це було завдяки використаному я URL-адресу

мій URL виглядав як / ui / project /: parameter1 /: parameter2.

Мені не потрібен параметр2 у всіх випадках зміни стану. У випадках, коли мені не знадобився другий параметр, мій URL буде виглядати як / ui / project /: parameter1 /. І тому всякий раз, коли у мене була зміна стану, у мене буде двічі оновлюватися контролер.

Рішенням було встановити параметр2 як порожній рядок і змінити стан.


1

У мене ця подвійна ініціалізація трапилася з іншої причини. Для деяких переходів маршрутів у моїй програмі я хотів примусити прокручування до верхньої частини сторінки (наприклад, у сторінових результатах пошуку ... натискання на наступний має перенести вас до верхньої частини сторінки 2).

Я зробив це, додавши слухача, до $rootScope $on $viewContentLoadedякого (на основі певних умов) виконується

$location.hash('top');

Мимоволі це призвело до переоцінки моїх маршрутів та реініціалізації контролерів



1

У моєму випадку перейменування контролера на іншу назву вирішило проблему.

Був конфлікт імен контролерів з « незграбно-UI дерева модуля»: я перейменував мій контролер від «CatalogerTreeController» до « TreeController » , а потім цей контролер починає ініціюватися двічі на сторінці , де « UI-дерево директива» використовується тому , що ця директива використовує контролер з назвою " TreeController ".


1

Для тих, хто використовує синтаксис ControllerAs, просто оголосіть мітку контролера в $ routeprovider наступним чином:

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

або

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

Після оголошення $ routeprovider не подайте контролер як у вікні. Натомість використовуйте мітку у вікні перегляду.


0

У мене була така ж проблема, і, спробувавши всі відповіді, я нарешті виявив, що у мене є директива, на мою думку, яка прив’язана до того ж контролера.

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

Після видалення директиви контролер перестав дзвонити двічі. Тепер моє запитання полягає в тому, як можна без цієї проблеми використовувати цю директиву, пов'язану з областю контролера?


0

Я щойно вирішив своє, що насправді було дуже невтішно. Це іонний гібридний додаток, я використовував ui-роутер v0.2.13. У моєму випадку є читач epub (використовуючи epub.js), який постійно повідомляв "жодного документа не знайдено", коли я перейду до своєї бібліотеки книг і виберіть будь-яку іншу книгу. Коли я перезавантажував книгу, браузер видавався ідеально, але коли я вибрав іншу книгу, знову з’явилася та сама проблема.

Моє рішення було дуже простим. Я тільки що видалив reload:trueз $state.go("app.reader", { fileName: fn, reload: true });в моємуLibraryController


0

У мене є те саме питання angular-route@1.6.7, і це тому, що додаткова коса риса в кінці маршруту виразів:

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

до

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

і воно працює правильно.


0

Просто додаю тут і свою справу:

Я використовував angular-ui-router з$state.go('new_state', {foo: "foo@bar"})

Після того, як я додав encodeURIComponent до параметру, проблема зникла: $state.go('new_state', {foo: encodeURIComponent("foo@bar")}).

Що трапилось? Символ "@" у значенні параметра не дозволений в URL-адресах. Як наслідок, angular-ui-router двічі створив мій контролер: під час першого створення він передав оригінальну "foo @ bar", під час другого створення він передав кодовану версію "foo% 40bar". Після явного кодування параметру, як показано вище, проблема усунулася.


-1

Я зрозумів, що мені дзвонять двічі, тому що я два рази викликав метод із мого 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>`

Виділений розділ змусив двічі викликати findX (). Сподіваюся, це комусь допоможе.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.