对抗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)。
Stewie

7
您能否解释“用手动引导”的含义?
体育节目


2
当我对日志记录问题进行故障排除并看到控制台日志触发两次时,我注意到我继承的应用程序中的重复控制器行为。第一个日志记录有一个值,但是第二个未定义。删除控制器的HTML ng-controller指令后,第二个未定义的控制台日志记录消失了。
Daniel Nalbach

2
如果angular.js被添加两次,那么这也可能发生
Mohit

Answers:


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">...,这个问题对我来说就停止了。我以前没有遇到过这种行为,但是也许其他人也有这种情况。
2013年

5
ngController的文档docs.angularjs.org/api/ng/directive/ngController中对此有很好的解释
查尔斯

1
有什么方法可以在路由中声明一个控制器,然后在html页面本身上声明另一个控制器?如何确保既可行又无冲突?还是这是不必要的?
思想家

1
可能是不必要的,也许使用几个指令代替?
格雷格

2
@Josh将其保留在HTML中会降低灵活性。您将模板直接绑定到一个控制器。您可以将ctrl用作路由的语法。
格雷格

127

AngularJS文档-ngController
注意,您还可以通过$ route服务在路由定义中声明控制器,从而将控制器附加到DOM。一个常见的错误是在模板本身中使用ng-controller再次声明控制器。这将导致控制器被附加并执行两次。

当您将ngRoute与ng-view指令一起使用时,默认情况下,控制器将附加至该dom元素(如果使用ui-router,则为ui-view)。因此,您无需再次将其附加在模板中。


重复的答案。作者已经回答说同样的话。只需向下滚动直到页面末尾。
Iago

4
我发现作者将其作为旁注感到困惑,尽管这显然是正确的方法。文档中的报价清楚地回答了这个问题。而且我敢肯定,很多人会发现文档的链接很有帮助。
shxfee

这解决了我的控制器被执行两次的问题。我所做的就是删除模板中的ng-controller,现在仅执行一次。
torbenrudgaard

70

我刚刚经历了这个问题,但是问题与公认的答案有所不同。我真的要把这留给我自己,以包括我为修复它所经历的步骤。

  1. 删除冗余控制器声明
  2. 检查路线中的尾部斜杠
  3. 检查 ng-ifs
  4. 检查是否有不必要的包装ng-view调用(我不小心留在了ng-view包装实际包装的ng-view。这导致对控制器的三个调用。)
  5. 如果您在Rails上,则应turbolinksapplication.js文件中删除gem 。我浪费了一整天才发现这一点。在这里找到答案
  6. 使用ng-app和bootstrap初始化应用两次。 对抗AngularJS执行控制器两次
  7. 当在指令的“链接”功能的整个元素上使用$ compile时,指令的功能也已定义了自己的控制器,并通过ng-click等在模板中使用此控制器的回调。在此处找到了答案。

5.如果您在Rails上,则应turbolinksapplication.js文件中删除gem 。那让我发疯,整日浪费时间去发现这一点。真痛。找到答案在这里
18augst,2015年

6.使用ng-app和bootstrap初始化应用两次。stackoverflow.com/questions/15535336/…–
thadeuszlay

1
谢谢 。对我来说,行之有效的是在路线尽头放下斜线
行之有效的 Pramod Sharma

谢谢一百万,我为此感到头疼。最终排名第七!(总是最后一个...)。
拉比·舒基·古尔

哇,信不信由你,我仔细阅读了您列表中的所有内容,但问题仍然存在。
雅各布·斯塔姆

14

只想在控制器可以两次初始化时再添加一种情况(这是angular.js 1.3.1的实际情况):

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

在这种情况下,当ng-view初始化时,$ route.current将已经被设置。这会导致双重初始化。

要解决此问题,只需将ng-if更改为ng-show / ng-hide,一切都会很好。


它帮助了我,我使用的是ng-show / ng-hide而不是ng-if,我有两个ng-view,这不是问题,但是ng-if禁用,而ng-show / ng-hide没问题
Dimitri Kopriwa

万分感谢。这种方法解决了我的问题。ui-view两次调用相同的控件,两次调用控制器。
curlreggie 2015年

7

补充说明:

也可以通过在页面上运行的指令中引用控制器来导致执行双控制器代码。

例如

return {

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

当您在HTML中也有ng-controller =“ myController”时


解决方案是什么?
Sagar Bhosale

1
仅使用其中一个。
gb2d


6

在这个问题上,我撕裂了我的应用程序及其所有依赖项(详细信息:AngularJS应用程序启动两次(尝试了通常的解决方案..)

最后,这完全是Batarang Chrome插件的问题。

解决此答案的方法

我强烈建议任何人列表上的第一件事是在更改代码之前,根据每个帖子将其禁用。


这是我的问题。该插件运行Angular应用程序的double实例,因此它可以提供范围信息(不知道为什么不能只使用初始实例)。
rgdigital 2015年

我很高兴它有所帮助,花了我很长的时间才弄清楚这一点。
刘易斯

5

如果您知道控制器无意中执行了多次,请尝试在文件中搜索有问题的控制器的名称,例如:search:MyController遍历所有文件。可能它已复制粘贴到其他html / js文件中,而当您开发或使用这些部件/控制器时却忘记了对其进行更改。资料来源:我犯了这个错误


5

在一个简单的应用程序中(没有路由和简单的ng-controller引用),我遇到了同样的问题,而我的控制器的构造函数确实运行了两次。最后,我发现我的问题是以下在Razor视图中自动引导AngularJS应用程序的声明

<html ng-app="mTest1">

我也使用angular.bootstrap手动启动了它

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

所以删除其中一个,对我有用。


4

在某些情况下,当您根本不正确关闭指令时,指令将运行两次,如下所示:

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

这将运行您的指令两次。当指令执行两次时,还有另一种常见情况:

确保您没有在index.html TWICE中包含指令!


另外,如果您使用的是UI-Router,则在类内为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

我遇到的问题可能是切线的,但是由于谷歌搜索使我想到了这个问题,因此这可能是适当的。使用UI路由器时,该问题使我感到头昏眼花,但仅当我尝试使用浏览器刷新按钮刷新页面时才出现。该应用程序使用具有父抽象状态的UI路由器,然后子状态关闭父状态。在应用程序run()功能上,有一个$state.go('...child-state...')命令。父州使用resolve,起初我认为子控制器可能执行两次。

在URL附加了哈希之前,一切都很好。
www.someoldwebaddress.org

然后,一旦URL被UI路由器修改,
www.someoldwebaddress.org#/childstate

...然后,当我使用浏览器刷新按钮刷新页面时$stateChangeStart触发两次,每次指向childstate

resolve父状态是什么开火两次。

也许这是一个矛盾。无论如何,这似乎确实消除了我的问题:在$stateProvider第一次调用的代码区域中,首先检查window.location.hash是否为空字符串。如果是这样,那一切就很好了。如果不是,则将window.location.hash设置为空字符串。然后似乎$state只有一次尝试去某个地方一次而不是两次。

另外,如果您不想依赖应用程序的默认值runstate.go(...),则可以尝试捕获哈希值,并使用哈希值来确定页面刷新之前所处的子状态,并在您的区域中添加条件设置的代码state.go(...)


1

就我而言,这是因为我使用了url模式

我的网址就像/ ui / project /:parameter1 /:parameter2。

在状态更改的所有情况下,我都不需要paramerter2。在不需要第二个参数的情况下,我的网址应为/ ui / project /:parameter1 /。因此,每当我进行状态更改时,我的控制器就会刷新两次。

解决方案是将parameter2设置为空字符串并进行状态更改。


1

我曾经有过两次双重初始化的原因。对于我的应用程序中的某些路线转换,我想强制滚动到页面顶部附近(例如,在分页的搜索结果中……单击“下一步”应将您带到页面2的顶部)。

我是通过向$rootScope $on $viewContentLoaded执行的which 添加一个侦听器来实现的(基于某些条件)

$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

只需在这里添加我的案例:

我用角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()被调用两次。希望它可以帮助某人。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.