对抗AngularJS执行控制器两次

Posted

技术标签:

【中文标题】对抗AngularJS执行控制器两次【英文标题】:Combating AngularJS executing controller twice 【发布时间】:2013-03-10 05:50:21 【问题描述】:

我了解 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 条记录插入我的数据库。我显然仍在学习,因为我多年来一直在努力反对这个!

【问题讨论】:

如果你的控制器运行了两次然后检查你没有初始化你的 Angular 应用程序两次(通过使用ng-app 和手动引导程序自动初始化它)。还要检查您是否已将控制器附加到多个元素(使用 ng-controller)。 你能解释一下“和手动引导”是什么意思吗? docs.angularjs.org/guide/bootstrap 在解决日志记录问题并看到控制台日志触发两次时,我注意到我继承的应用程序中的重复控制器行为。第一个日志火灾有一个值,但第二个未定义。删除控制器的 html ng-controller 指令后,未定义的第二个控制台日志触发消失了。 如果 angular.js 被添加了两次,那么这也可能发生 【参考方案1】:

应用路由器指定导航到MyController,如下所示:

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

但我在home.html也有这个:

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

这消化了控制器两次。从 HTML 中删除 data-ng-controller 属性解决了该问题。或者,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">

这也会导致控制器运行两次。

【讨论】:

这似乎是最好的离开这个地方,经过几个小时的梳理我的代码,不要问我怎么做,但是当我将&lt;div ng-view&gt;... 更改为&lt;div class="ng-view"&gt;... 时,这个问题对我来说停止了。我以前没有遇到过这种行为,但也许其他人也有这种行为。 在 ngController 的文档中对此有很好的解释,docs.angularjs.org/api/ng/directive/ngController 有没有办法在路由中声明一个控制器,然后在html页面本身上声明另一个控制器?如何确保两者都有效且没有冲突?或者这是不必要的? 这可能是不必要的,也许用几个指令代替? @Josh 将其留在 HTML 中会降低灵活性。您将模板直接绑定到一个控制器。您可以使用 ctrl 作为路由的语法。【参考方案2】:

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

当您将 ngRoute 与 ng-view 指令一起使用时,控制器默认附加到该 dom 元素(如果您使用 ui-router,则附加到 ui-view)。所以你不需要在模板中再次附加它。

【讨论】:

重复的答案。作者已经回复了同样的说法。只需向下滚动到页面末尾即可。 我发现作者将其作为旁注令人困惑,而这显然是正确的做法。文档中的引用清楚地回答了这个问题。我相信很多人会发现该文档的链接很有帮助。 这解决了我的控制器被执行两次的问题。我所做的只是删除模板中的 ng-controller,现在它只执行一次。【参考方案3】:

我刚刚经历了这个,但问题与接受的答案不同。我真的要把它留在这里,为了我未来的自己,包括我修复它所经历的步骤。

    Remove redundant controller declarations Check trailing slashes in routes Check for ng-ifs 检查是否有任何不必要的包装 ng-view 调用(我不小心留下了一个 ng-view,它包装了我的实际 ng-view。这导致对我的控制器进行了三个调用。) 如果你在 Rails 上,你应该从你的 application.js 文件中删除 turbolinks gem。我浪费了一整天的时间来发现这一点。找到答案here。 使用 ng-app 和引导程序初始化应用程序两次。 Combating AngularJS executing controller twice 在指令的“链接”函数中对整个元素使用 $compile 时,该指令还定义了自己的控制器,并通过 ng-click 等在模板中使用此控制器的回调。找到答案 here。

【讨论】:

5.如果你在 Rails 上,你应该从你的 application.js 文件中删除 turbolinks gem。这让我发疯,浪费了一整天的时间来发现这一点。真正的痛苦。找到答案here 6.使用 ng-app 和 bootstrap 初始化应用程序两次。 ***.com/questions/15535336/… 谢谢。对我来说,有效的方法是在路线的尽头加上斜线 谢谢一百万,这让我头疼。最终成为第 7 名! (总是最后一个......)。 哇,信不信由你,我浏览了您列表中的所有内容,但问题仍然存在。【参考方案4】:

只是想在控制器可以初始化两次的情况下再添加一种情况(这对于 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 没有 非常感谢。这种方法解决了我的问题。调用了两次相同的ui-view,这两次调用了控制器。【参考方案5】:

想补充以供参考:

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

例如

return 

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

当你的 HTML 中也有 ng-controller="myController" 时

【讨论】:

解决办法是什么? 只使用其中一种。【参考方案6】:

在 Angular 1.3+ 中使用 angular-ui-router 时,出现了关于 Rendering views twice on route transition 的问题。这也导致两次执行控制器。建议的解决方案都不适合我。

但是,将 angular-ui-router 从 0.2.11 更新到 0.2.13 为我解决了问题。

【讨论】:

【参考方案7】:

我在这个问题上撕毁了我的应用程序及其所有依赖项(详情请点击此处:AngularJS app initiating twice (tried the usual solutions..))

最后,都是 Batarang Chrome 插件的错。

this answer中的解析:

我强烈建议任何人的第一件事是在更改代码之前根据帖子禁用它。

【讨论】:

这是我的问题。该插件运行您的 Angular 应用程序的双重实例,因此它可以提供范围信息(不知道为什么它不能只使用初始实例)。 我很高兴它有帮助,我花了很长时间才弄明白。【参考方案8】:

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

【讨论】:

【参考方案9】:

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

<html ng-app="mTest1">

我还使用 angular.bootstrap 手动引导它,即

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

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

【讨论】:

【参考方案10】:

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

&lt;my-directive&gt;Some content&lt;my-directive&gt;

这将运行你的指令两次。 还有另一种常见的情况是你的指令运行两次:

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

【讨论】:

另外,如果您使用的是 UI-Router,在类中为 ui-view 指定指令名称似乎会导致双重执行。将其更改为 E 或 可修复双重执行问题。【参考方案11】:

一直在用 AngularJS 1.4 rc build 解决这个问题,然后意识到上述答案都不适用,因为它在撰写本文时源自new router library for Angular 1.4 and Angular 2。因此,我在这里为可能正在使用新的 Angular 路由库的任何人做一个说明。

基本上,如果 html 页面包含用于加载应用程序部分的 ng-viewport 指令,则通过单击在 ng-link 中指定的超链接将导致关联组件的目标控制器被加载两次。细微的差别在于,如果浏览器已经加载了目标控制器,通过重新单击相同的超链接只会调用一次控制器。

还没有找到可行的解决方法,尽管我相信这种行为与shaunxu 提出的观察一致,希望这个问题能在未来构建新的路由库和 AngularJS 1.4 版本中得到解决。

【讨论】:

【参考方案12】:

就我而言,我发现两个视图使用同一个控制器。

$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'
    
  
);

【讨论】:

【参考方案13】:

我遇到的问题可能是切题的,但由于谷歌搜索让我想到了这个问题,这可能是合适的。使用 UI Router 时,这个问题对我来说很丑陋,但只有当我尝试使用浏览器刷新按钮刷新页面时。该应用程序使用具有父抽象状态的 UI 路由器,然后使用父抽象状态的子状态。在应用程序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 只尝试去某个地方一次而不是两次。

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

【讨论】:

【参考方案14】:

对于那些使用 ControllerAs 语法的人,只需在 $routeprovider 中声明控制器标签,如下所示:

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

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

在声明 $routeprovider 之后,不要像在视图中那样提供控制器。而是使用视图中的标签。

【讨论】:

【参考方案15】:

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

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

在所有状态变化的情况下,我都不需要 paramerter2。在我不需要第二个参数的情况下,我的 url 将类似于 /ui/project/:parameter1/。所以每当我有状态改变时,我都会让我的控制器刷新两次。

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

【讨论】:

【参考方案16】:

出于不同的原因,我进行了这种双重初始化。对于我的应用程序中的一些路由转换,我想强制滚动到页面顶部附近(例如,在分页搜索结果中......单击下一步应该会将您带到第 2 页的顶部)。

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

$location.hash('top');

无意中这导致我的路线被重新评估并且控制器被重新初始化

【讨论】:

【参考方案17】:

我的问题是像这样更新搜索参数$location.search('param', key);

您可以在这里阅读更多信息

controller getting called twice due to append params in url

【讨论】:

【参考方案18】:

在我的例子中,将控制器重命名为不同的名称解决了这个问题。

与“angular-ui-tree”模块存在控制器名称冲突:我将控制器从“CatalogerTreeController”重命名为“TreeController em>”,然后该控制器开始在使用“ui-tree”指令的页面上启动两次,因为该指令使用名为“TreeController”的控制器。

【讨论】:

【参考方案19】:

我遇到了同样的问题,在尝试了所有答案后,我终于发现我的视图中有一个指令绑定到同一个控制器。

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

);

删除指令后,控制器停止被调用两次。现在我的问题是,如何在没有这个问题的情况下使用绑定到控制器范围的这个指令?

【讨论】:

【参考方案20】:

我刚刚解决了我的问题,这实际上非常令人失望。它是一个离子混合应用程序,我使用了 ui-router v0.2.13。在我的例子中,有一个 epub 阅读器(使用 epub.js)在我导航到我的图书库并选择任何其他书时不断报告“未找到文档”。当我重新加载浏览器的书时完美呈现,但是当我选择另一本书时又遇到了同样的问题。

我的解决方法很简单。我刚刚在我的LibraryController 中从$state.go("app.reader", fileName: fn, reload: true ); 中删除了reload:true

【讨论】:

【参考方案21】:

我在angular-route@1.6.7 中遇到了同样的问题,这是因为正则表达式路径末尾的 额外斜杠

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

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

它工作正常。

【讨论】:

【参考方案22】:

在这里也添加我的案例:

我使用 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”。一旦我如上所示显式编码参数,问题就消失了。

【讨论】:

【参考方案23】:

我的问题真的很难追查。最后,当网页缺少图像时,问题就出现了。 src 缺少 URL。这发生在 MVC 5 Web 控制器上。为了解决这个问题,我在没有可用的真实图像时添加了透明图像。

<img  class="logo" src="">

【讨论】:

【参考方案24】:

我发现我的方法被调用了两次是因为我从我的 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() 被调用两次。希望它可以帮助某人。

【讨论】:

以上是关于对抗AngularJS执行控制器两次的主要内容,如果未能解决你的问题,请参考以下文章

AngularJs:使用 $routeProvider 调用控制器两次

AngularJS:从下拉列表中选择任何选项时,函数在 ng-change 上触发两次

如何在页面加载时执行 AngularJS 控制器功能?

iOS Chrome 和 AngularJS 不在 HEAD 中执行控制器

使用 AngularJS,如何在视图和控制器同步特定更改后触发代码执行?

可能的控制器或至少执行两次的功能