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

Greg
источник
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">

Это также приведет к запуску контроллера дважды.

Greg
источник
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). Таким образом, вам не нужно будет прикреплять его снова в шаблоне.

shxfee
источник
Дублированный ответ. Автор уже ответил, сказав то же самое. Просто прокрутите вниз до конца страницы.
Яго
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'-функции директивы , которая также имеет свой собственный контроллер , определенные и использует обратные вызовы этого контроллера в шаблон через нг клик и т.д. Найдено ответ здесь .
JesseBuesking
источник
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, и все будет работать хорошо.

DontRelaX
источник
Это помогло мне, я использовал 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

gb2d
источник
какое решение для этого?
Сагар Бхосейл
1
Используйте только один или другой.
gb2d
7

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

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

fracz
источник
6

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

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

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

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

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

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

user3901016
источник
5

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

<html ng-app="mTest1">

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

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

так что убрав один из них, он сработал для меня.

athina.bikaki
источник
4

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

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

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

убедитесь, что вы не включили вашу директиву в свой index.html ДВАЖДЫ !

pleerock
источник
Также, если вы используете 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.

codeful.element
источник
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(...).

mg1075
источник
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 ».

AlexDEV.pro
источник
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

maksbd19
источник
0

У меня та же проблема angular-route@1.6.7, и это из-за дополнительной косой черты в конце маршрута регулярного выражения:

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

в

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

и это работает правильно.

B.Ma
источник
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 () дважды. Надеюсь, это кому-нибудь поможет.

Нанду Праджапати
источник