Борьба с 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-controller).
— Стьюи

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">...эту проблему, эта проблема для меня прекращается. Я не сталкивался с таким поведением раньше, но, возможно, кто-то еще имеет это.
— Phix

5
Это хорошее объяснение можно найти в документах для ngController, docs.angularjs.org/api/ng/directive/ngController
— Charles

1
Есть ли способы объявить контроллер в маршруте, а затем другой контроллер на самой html-странице? Как обеспечить работоспособность и отсутствие конфликтов? Или это не нужно?
— Thinkerer

1
Это, вероятно, не нужно, может быть, вместо этого использовать пару директив?
— Грег

2
@ Джош, оставляя это в HTML, уменьшает гибкость. Вы привязываете свой шаблон непосредственно к одному контроллеру. Вы можете использовать Ctrl в качестве синтаксиса с маршрутизацией.
— Грег

127

AngularJS docs - ngController
Обратите внимание, что вы также можете подключить контроллеры к DOM, объявив его в определении маршрута через службу $ route. Распространенной ошибкой является повторное объявление контроллера с использованием ng-controller в самом шаблоне. Это приведет к тому, что контроллер будет подключен и выполнен дважды.

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


Дублированный ответ. Автор уже ответил, сказав то же самое. Просто прокрутите вниз до конца страницы.
— Яго

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, вы должны удалить turbolinksgem из вашего application.jsфайла. Это сделало меня сумасшедшим, потратил впустую целый день, чтобы обнаружить это Настоящая боль. Нашел ответ здесь
— 18

6. Инициализируйте приложение дважды с помощью ng-app и с помощью начальной загрузки. stackoverflow.com/questions/15535336/…
— thadeuszlay

1
Спасибо . Для меня то, что сработало, - это поставить косую черту в конце маршрута
— Прамод Шарма,

Спасибо миллион, ломал голову над этим. в конечном итоге номер 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-hide, и все будет работать хорошо.


Это помогло мне, я использовал ng-show / ng-hide вместо ng-if, у меня было два ng-view, это не проблема, но отключение ng-if, а ng-show / ng-hide
— Dimitri Коприва

Благодаря тонну. Этот подход решил мою проблему. Звонил один и тот же ui-viewдважды, который дважды вызывал контроллер.
— Curlyreggie

7

Хотел бы добавить для справки:

Двойное выполнение кода контроллера также может быть вызвано ссылкой на контроллер в директиве, которая также выполняется на странице.

например

return {

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

Когда у вас также есть ng-controller = "myController" в вашем HTML


какое решение для этого?
— Сагар Бхосейл

1
Используйте только один или другой.
— gb2d

7

При использовании angular-ui-router с Angular 1.3+ возникла проблема с рендерингом представлений дважды при переходе по маршруту . Это также привело к выполнению контроллеров дважды. Ни одно из предложенных решений не сработало для меня.

Однако обновление angular-ui-routerс 0.2.11 до 0.2.13 решило проблему для меня.


6

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

И в конце концов, во всем виноват плагин Batarang Chrome.

Разрешение в этом ответе :

Я настоятельно рекомендую первым делом отключить его для каждого сообщения перед изменением кода.


Это была моя проблема. Плагин запускает двойной экземпляр вашего приложения Angular, поэтому он может предоставить информацию о сфере действия (не знаю, почему он не может просто использовать начальный экземпляр).
— rgdigital

Я рад, что это помогло, мне понадобилось много времени, чтобы понять это.
— Льюис

5

Если вы знаете, что ваш контроллер непреднамеренно выполняет более одного раза, попробуйте выполнить поиск в ваших файлах имени нарушающего контроллера, например: search: 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-Router, указание имени директивы для ui-view внутри класса может привести к двойному выполнению. Изменение его на E или <ui-view> </ ui-view> устраняет проблему двойного выполнения.
— SteveGSD

2

Я ломал голову над этой проблемой со сборкой AngularJS 1.4 rc, а затем понял, что ни один из вышеперечисленных ответов не применим, поскольку он был создан из новой библиотеки маршрутизатора для Angular 1.4 и Angular 2 на момент написания этой статьи. Поэтому я оставляю здесь заметку для тех, кто может использовать новую библиотеку угловых маршрутов.

По сути, если 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 Router у меня возникает проблема, но только когда я пытаюсь обновить страницу кнопкой обновления браузера. Приложение использует UI Router с родительским абстрактным состоянием, а затем дочерние состояния от родительского. В run()функции приложения есть $state.go('...child-state...')команда. Родительское состояние использует a resolve, и сначала я подумал, что, возможно, дочерний контроллер выполняется дважды.

Все хорошо, пока к URL не добавлен хеш.
www.someoldwebaddress.org

Затем, как только URL был изменен UI Router,
www.someoldwebaddress.org#/childstate

... а затем, когда я обновляю страницу с помощью кнопки обновления браузера , $stateChangeStartогонь срабатывает дважды, и каждый раз указывает на childstate.

На resolveродительском состоянии запускается дважды.

Возможно, это клудж; несмотря на это, это, кажется, устраняет проблему для меня: в области кода, где $stateProviderвызывается впервые, сначала проверьте, является ли window.location.hash пустой строкой. Если это так, все хорошо; если это не так, установите для window.location.hash пустую строку. Тогда кажется, что $stateединственная попытка пойти куда-то один раз, а не дважды.

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


1

В моем случае это было из-за шаблона URL, который я использовал

мой URL был похож на / ui / project /: параметр1 /: параметр2.

Мне не нужен paramerter2 во всех случаях изменения состояния. В тех случаях, когда мне не нужен второй параметр, мой URL будет выглядеть как / ui / project /: parameter1 /. И поэтому всякий раз, когда у меня было изменение состояния, мой контроллер обновлялся дважды.

Решением было установить параметр2 как пустую строку и выполнить изменение состояния.


1

У меня была двойная инициализация по другой причине. Для некоторых маршрутов-переходов в моем приложении я хотел принудительно прокрутить до верхней части страницы (например, в результатах поиска с разбивкой по страницам ... нажатие кнопки «Далее» должно привести вас к началу страницы 2).

Я сделал это, добавив слушателя к $rootScope $on $viewContentLoadedкоторому (на основе определенных условий) выполняется

$location.hash('top');

Случайно это привело к переоценке моих маршрутов и повторной инициализации контроллеров.



1

В моем случае переименование контроллера на другое имя решило проблему.

Возник конфликт имен контроллеров с модулем « angular-ui-tree »: я переименовал свой контроллер из «CatalogerTreeController» в « TreeController », а затем этот контроллер дважды запускается на странице, где используется директива « ui-tree », поскольку эта директива использует контроллер с именем « 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-router 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-роутер с$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.