Борьба с AngularJS, выполняющим контроллер дважды

520

Я понимаю, что 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 записи в мою БД. Я, очевидно, все еще участвую, поскольку я уже давно натыкаюсь на эту голову!

  • 97
    Если ваш контроллер работает дважды, убедитесь, что вы не инициализируете приложение Angular дважды (автоматически инициализируя его с помощью ng-app и с помощью ручной загрузки). Также проверьте, подключен ли ваш контроллер к нескольким элементам (с помощью ng-controller).
  • 6
    Можете ли вы объяснить, что вы подразумеваете под "и с ручной загрузкой"?
Показать ещё 3 комментария
Теги:

20 ответов

950
Лучший ответ

Маршрутизатор приложения указал навигацию на 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">... , этот вопрос остановился для меня. Я не сталкивался с таким поведением раньше, но, возможно, кто-то еще имеет это.
  • 4
    Это хорошее объяснение можно найти в документах по ngController, docs.angularjs.org/api/ng/directive/ngController.
Показать ещё 12 комментариев
114

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

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

  • 0
    Дублированный ответ. Автор уже ответил, сказав то же самое. Просто прокрутите вниз до конца страницы.
  • 3
    Меня смутило то, что автор добавил это как примечание, хотя это, очевидно, правильный способ сделать это. Цитата из документов четко отвечает на вопрос. И я уверен, что многие сочтут ссылку на документ полезной.
Показать ещё 1 комментарий
58

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

  • 0
    5. если вы работаете с Rails, вы должны удалить гем turbolinks из файла application.js . Это сделало меня сумасшедшим, потратил впустую целый день, чтобы обнаружить это. Настоящая боль. Нашел ответ здесь
  • 0
    6. Инициализируйте приложение дважды с помощью ng-app и с помощью начальной загрузки. stackoverflow.com/questions/15535336/...
Показать ещё 4 комментария
11

Просто хочу добавить еще один случай, когда контроллер может инициализировать дважды (это актуально для 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 и все будет работать хорошо.

  • 0
    Это помогло мне, я использовал ng-show / ng-hide вместо ng-if, у меня было два ng-view, это не проблема, но отключить ng-if, в то время как ng-show / ng-hide этого не делает
  • 0
    Благодаря тонну. Этот подход решил мою проблему. Дважды вызывал один и тот же ui-view который дважды вызывал контроллер.
7

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

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

7

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

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

например.

return {

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

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

  • 0
    какое решение для этого?
  • 1
    Используйте только один или другой.
5

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

И, в конце концов, это была ошибка Bartang Chrome.

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

Изображение 3645

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

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

У меня была такая же проблема, в простом приложении (без маршрутизации и простой ссылки на ng-controller), и мой конструктор контроллера выполнялся дважды. Наконец, я узнал, что моя проблема заключалась в следующем объявлении автозагрузки моего приложения AngularJS в моем представлении Razor

<html ng-app="mTest1">

Я также вручную загрузил его с помощью angular.bootstrap i.e.

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

поэтому удалив один из них, он работал у меня.

4

В некоторых случаях ваша директива выполняется дважды, когда вы просто не исправляете свою директиву так:

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

Это приведет к удвоению вашей директивы. Также есть еще один случай, когда ваша директива выполняется дважды:

убедитесь, что вы не указали свою директиву в index.html TWICE!

  • 0
    Также, если вы используете UI-Router, указание имени директивы для ui-view внутри класса может привести к двойному выполнению. Изменение его на E или <ui-view> </ ui-view> устраняет проблему двойного выполнения.
4

Если вы знаете, что ваш контроллер непреднамеренно выполняет несколько раз, попробуйте выполнить поиск через ваши файлы для имени нарушающего контроллера, например: search: MyController через все файлы. Вероятно, он был скопирован в какой-то другой файл html/js, и вы забыли его изменить, когда вам нужно было разработать или использовать эти частичные/контроллеры. Источник: Я сделал эту ошибку

2

Проблема, с которой я сталкиваюсь, может быть тангенциальной, но, поскольку поисковая оптимизация привела меня к этому вопросу, это может быть уместно. Проблема вызывает у меня уродливую голову, когда я использую UI Router, но только когда я пытаюсь обновить страницу кнопкой обновления браузера. Приложение использует UI-маршрутизатор с родительским абстрактным состоянием, а затем дочерние элементы от родителя. В функции app run() есть команда $state.go('...child-state...'). В родительском состоянии используется resolve, и сначала я думал, что, возможно, дочерний контроллер выполняет дважды.

Все в порядке, прежде чем URL-адрес будет содержать хэш.
www.someoldwebaddress.org

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

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

resolve в родительском состоянии - это то, что срабатывает дважды.

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

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

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

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

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

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

1

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

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

$location.hash('top');

Неосторожно это приводило к переоценке моих маршрутов и повторному инициализации контроллеров.

1

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

мой url был как /ui/project/: parameter1/: parameter2.

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

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

0

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

Существовал конфликт имен контроллеров с модулем "angular -ui-tree": я переименовал мой контроллер из "CatalogerTreeController" в "TreeController", а затем этот контроллер запускается дважды на странице, где используется директива "ui-tree", поскольку в этой директиве используется контроллер с именем "TreeController".

0

Я понял, что мой вызов вызывает дважды, потому что я дважды вызывал метод из моего 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() дважды. Надеюсь, это поможет кому-то.

0

Я только что решил мой, что было на самом деле довольно разочаровывающим. Его ионное гибридное приложение, я использовал ui-router v0.2.13. В моем случае есть epub reader (с использованием epub.js), который постоянно сообщал "не найденный документ", как только я перехожу к библиотеке книг и выбираю любую другую книгу. Когда я перезагрузился, книга браузера была отлично отрисована, но когда я выбрал другую книгу, снова возникла проблема.

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

0

Моя проблема заключалась в обновлении параметров поиска, таких как $location.search('param', key);

вы можете прочитать об этом здесь

контроллер получает вызов дважды из-за добавления параметров в URL

0

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

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

После удаления директивы контроллер перестает вызываться дважды. Теперь мой вопрос: как использовать эту директиву, связанную с областью контроллера без этой проблемы?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню