【问题标题】:Why does my watcher gets called twice on the same change?为什么我的观察者在同一个更改上被调用两次?
【发布时间】:2016-03-07 19:13:48
【问题描述】:

我有一个 AngularJS 1.4* 应用程序在本地运行(还)。此应用由 Laravel 5.1 后端 RESTFul API 提供服务。

我必须制作这个代表旅行团的应用程序。一个包由天组成,范围从0到N天。每天都有一个服务列表,从 0 到 N 个服务。还有一家酒店。

我的 web 服务器,我的 laravel 应用程序从中消费,它为我提供了一个预先设置的包,其中包含一个日期列表:每个日期都有一个服务列表和一个酒店数据(目前未使用)。在响应中,我有一个包的属性列表(现在不重要)和一个天数组,称为days_info。该响应被放入我的PackageController 上的$scope.packagePackageController 还声明了一个名为 packageBlock 的指令,它包含日期列表和包的一些其他数据。

<div ng-repeat="day in package.days_info" class='row'>
    <div class='col-md-12'>
        <package-days-block></package-days-block>
    </div>
</div>

&lt;package-days-block&gt; 指令中,我每天都有另一个来遍历里面的服务列表。

<div class='container-fluid' ng-repeat='service in day.services' ng-controller="ServiceController">
    <service-block></service-block>
</div>

这就是问题开始的地方:令我不解的是,我现在在我的ServiceController 中有一个$scope.service。因此,我开始通过$scope.serviceServiceController 中根据需要更改它。

$scope.service 有一个名为service_id 的属性。我在上面放了一个监听器/观察器,所以在任何时候 $scope.service.service_id 发生变化时,我都会要求另一个 service_table (保存有关服务的信息,它基于 service_id之前由用户选择或更改),并将其放在$scope.service.table中。

// ServiceController
$scope.reloadServicesTable = function(service_id, service_day, date, paxes){
    MandatoryService.getServiceTable(service_id, service_day, date, paxes)
    .then(
        function(service_data) {
            $scope.service.table = service_data;
        }, 
        ...
    );

reloadServicesTableservice_id 更改的观察者上被调用。

// ServiceController
$scope.$watch(
    'service.service_id', // Places the watcher to watch the changes on the service's ID.
    function(new_service, old_service) {
        if( new_service === old_service )
            return; 

        $scope.reloadServicesTable($scope.service.service_id, $scope.service.service_day, $scope.day.date, $scope.package.paxes);

    }
);

问题从这里开始:当service_id只改变一次时,对服务表的请求被调用了两次。

为什么,上帝,为什么?!

在我的代码的另一部分,我从 PackageController 运行整个 days_info 数组并读取 service.table 中属性 price 的值:service.table.price .在那里,我意识到有两个范围:一个由我处理,另一个我没有 FREAKING IDEA 来自哪里!

如果我在通过 days_info 的方法中放置一个 console.log($scope);,我会为每个请求获得两个范围。此方法在 PackageController 上。

任何想法为什么会发生这种情况?

P.S.:这是我的第一个 AngularJS 应用程序,所以,如果我搞砸了一些基本的东西,请放轻松...

编辑:

正如 cmets 的一位同事所指出的,我的问题不是很可重复。可悲的是,我不能只把我有疑问的部分放在这里,因为我根本不知道问题出在哪里! (我知道这没什么帮助)

我从 Chrome 控制台截取了一些屏幕截图:

首先,更改 service_id 时触发的请求

如您所见,每个请求每次都会被调用两次。这不是一次性的。 /api/service/{id}... 是对服务表信息的调用。 /api/service/by_route/origin/... 返回从一个城市到另一个城市(或同一个城市)的服务列表。一个不干涉另一个。

另一张图片是来自 PackageController $scope 的 console.log 的输出,此时 service_id 正在更改。

如您所见,有两种不同的作用域。而b 作用域是r 作用域的儿子。 r 范围也在调用service_id 上的观察者?

总价的调用在不同的地方被调用了两次,如下图所示:

【问题讨论】:

  • 您的问题太长,包含很多不相关的信息,但缺少可重现的测试用例。所以很难理解问题到底是什么。但是文档说: *[...] 在极少数情况下,这是不可取的,因为当 watchExpression 的结果没有改变时调用了侦听器。要在侦听器 fn 中检测这种情况,您可以比较 newVal 和 oldVal。如果这两个值相同 (===),则由于初始化而调用了侦听器。
  • 通常情况下,使用手表是个坏主意。如果您以简单的方式告诉我们您想要实现的目标,我们可以帮助您找到更好的方法。
  • 我在比较函数后调用的 fn 中比较两个值(newValue 和 oldValue)。这似乎不是问题(不再)。由于该函数被系统地调用了两次!我将从我的请求网络日志中打印两个打印件和一个请求更改服务的范围。
  • 只是在黑暗中拍摄,但要确保 ServiceController 没有被实例化两次。此外,您有大约 8800 名观察者,这在性能方面非常糟糕。
  • @DennisBraga 你需要为这个问题创建一个minimal reproducible example,我检查了你在聊天中发布的fiddle,但只有js代码。我认为无法猜测您的设置有什么问题。它需要一些调试才能找到问题。

标签: angularjs angularjs-scope angularjs-ng-repeat


【解决方案1】:

它可能会解决您的问题。甚至我也遇到过和你说的一模一样的情况。

  1. 对我来说,原因是控制器再次初始化,其中编写了一个单独的 api 调用,用于最初加载页面。

  2. 还有一种情况是您在标记中分配了两次控制器。

【讨论】:

  • 这正是发生在我身上的事!你在 14 秒内没有得到赏金。 =/ 已经分配给罗伯特。我为 div 声明了一个 ng-controller 并在同一个控制器上调用了一个指令,因此实例化了它两次!这就是问题所在。
  • 我的情况与@DennisBraga 描述的完全一样,从视图中删除 ng-controller 的声明有助于解决问题。
【解决方案2】:
<div ng-repeat="day in package.days_info" class='row'>
    <div class='col-md-12'>
        <package-days-block day="day"></package-days-block>
    </div>
</div>

<div class='container-fluid' ng-repeat='service in day.services'>
    <service-block service="service"></service-block>
</div>

dayservice 传递到指令中。使用两种方式绑定将您的 dayservice 更改传递回 package.days_info。

删除您的ServiceController ng-repeat 控制器没有多大意义。 &lt;service-block&gt;&lt;package-days-block&gt; 是处理逻辑的 E 指令。

在你的PackageController中只写一个观察者来观察package.days_info 当你的dayservice 改变时,它可以简单地找出并做一些事情。

只需冷静并修复它。

【讨论】:

  • 问题不完全是这样。但是,小费很好!非常好。因为我可能会从现在开始使用它,所以我会给你赏金(并且它也会在 22 分钟内到期)。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-18
  • 1970-01-01
相关资源
最近更新 更多