【问题标题】:Angular resource call not fired when called from $scope.$watch从 $scope.$watch 调用时未触发 Angular 资源调用
【发布时间】:2013-07-30 05:36:24
【问题描述】:

我创建了一个指令来监视day 属性并在属性值更改时检索远程资源。 CoffeeScript 代码:

angular.module('app.directives').directive 'myDirective', ['$timeout', ($timeout)->

    DirectiveController=($scope, activityResource)->

        # Load activities
        load= ()->
            activityResource.get {
                day: $scope.day
                environment_id: $scope.environment.id
            },
            (data)->
                $scope.activities = data.activities

        # Watch environment and reload activities
        $scope.$watch 'environment', (value) ->
            load()

        # Watch selected day to reload activities
        $scope.$watch 'day', (value) ->
            load()
...
]

每次day 更改时都会调用load 方法,但不会触发结果回调。网络检查显示请求没有发送到远端。我发现的唯一解决方法是使用$timeout 服务推迟load 方法的执行:

        $scope.$watch 'day', (value) ->
            $timeout (()-> load()), 10

我怀疑存在与范围生命周期相关的问题,但我无法弄清楚为什么我必须推迟对资源的调用。

更新

资源源代码:

resources = angular.module('app.resources', ['ngResource'])

...

resources.factory 'app.activityResource', ['$resource', 'app.endpoint', ($resource, endpoint)->
  $resource "#{endpoint()}/user/environments/:environment_id/activities/:id/:verb",
  {environment_id: "@environment_id", id: "@id"},
  latest:
    method : 'GET'
    params:
      verb: 'latest'
  annual:
    method : 'GET'
    params:
      verb: 'annual'
]

我还在代码摘录中添加了第二个监视属性 (environment),用作查询资源的参数。

更新 2

我不知道它是否相关,但我们正在使用 CORS 来访问远程端(这似乎运作良好)。

【问题讨论】:

  • 是否可以将代码放在 plunkr 或 jsfiddle 中。用于调试代码的更简单的演示。
  • 你的activityResource是什么样的?
  • @RishabhSinghal 我试图创建一个 jsFiddle 但到目前为止还没有重现该问题。
  • @DotDotDot 我更新了问题。
  • 我尝试使用 $resource 的简化版本重现您的问题,而且它似乎每次都能正常工作并调用它,jsfiddle.net/DotDotDot/qUHGY/4

标签: angularjs coffeescript angularjs-directive angularjs-scope


【解决方案1】:

您没有看到请求被触发的原因是,作为 $http 调用的一部分,配置作为承诺传递给每个拦截器。

https://github.com/angular/angular.js/blob/b99d064b6ddbcc9f59ea45004279833e9ea82928/src/ng/http.js#L665-L670

在 Angular 中,$q 承诺仅在 $digest 循环开始时解决。因此,在 $digest 循环发生之前,您的 HTTP 请求甚至不会被触发,这就是为什么您会看到将 $timeout 包裹在它周围会有所帮助(超时会导致 $digest )。

https://github.com/angular/angular.js/blob/2a5c3555829da51f55abd810a828c73b420316d3/src/ng/q.js#L160 https://github.com/angular/angular.js/blob/3967f5f7d6c8aa7b41a5352b12f457e2fbaa251a/src/ng/rootScope.js#L503-L516

您还可以通过重新创建您看到的错误情况并观察网络请求来验证这一点。运行此命令后,您应该会看到您的请求被触发:

angular.element( "[ng-app]" ).scope().$digest()

该代码在 $rootScope 上触发 $digest 循环,导致您未解决的承诺得到解决。

有关更多讨论,请参阅此问题,因为我同意这是令人惊讶的行为。

https://github.com/angular/angular.js/issues/2881

【讨论】:

  • 感谢您的回答,我怀疑是这种问题。你知道为什么它在这个小提琴jsfiddle.net/jefmathiot/xCJjc/4 中起作用吗?我试图非常接近我们的实际用例(在指令中单击会更改触发 Ajax 调用的嵌套指令的属性),但我不明白为什么在这种特殊情况下会执行 $digest 循环。
  • 我在猜测的领域没有看到您的确切来源,但是如果您查看我发布到 rootScope 源的链接,您可以看到 asyncQueue 正在作为$digest 中的 do/while 循环。这实际上意味着,如果您的范围不是最后处理的范围,您可能会看到它按预期工作。如果它是最后处理的范围,则 asyncQueue 将不会被耗尽,直到下一个 $digest
  • 看来你猜得不错:),但我有点迷茫。在 rootScope 源代码中,do-while 循环似乎只要有元素进入 asyncQueue 就会被处理。调用资源不应该将元素附加到此队列中?
  • do/while 与对每个 $scope 进行脏检查相关联(参见 while 条件),但是在每次迭代开始时,asyncQueue 被清空。每个作用域共享相同的 asyncQueue,因为它们都继承自 $rootScope。如果您的 $scope 是 $digest 周期中正在处理的最后一个,它将向 asyncQueue 添加一些内容以便您的 deferred 得到解决,但是在 $digest 再次发生之前它不会被耗尽。希望这能澄清我认为可能正在发生的情况。您可以随时运行调试器并观察 asyncQueue 以了解发生了什么
  • 我运行了调试器(没有$timeout hack)。当day 属性发生变化时:首先$digest asyncQueue 为空,然后从load 方法调用资源,然后第二个$digest asyncQueue 仍然为空...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 2018-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-22
相关资源
最近更新 更多