【问题标题】:How can I get an asynchronous service call to update the view when it returns in AngularJS?当视图在 AngularJS 中返回时,如何获取异步服务调用来更新视图?
【发布时间】:2012-08-19 01:17:57
【问题描述】:

这是对我的previous AngularJS question 的跟进。

我正在尝试复制此视频教程中提到的 $resource 服务的功能:link

在本教程中,您可以使用异步调用更新范围变量及其视图。首先,这是设置的资源:

$scope.twitter = $resource('http://twitter.com/:action',
    {action: 'search.json', q: 'angularjs', callback: 'JSON_CALLBACK'},
    {get: {method: 'JSONP'}});

然后在资源上调用get函数来进行ajax调用并更新必要的作用域变量:

$scope.twitterResult = $scope.twitter.get();

这里发生的是$scope.twitter.get() 立即返回一个空对象引用。然后,当 ajax 请求返回时,该对象将使用来自 Twitter 的数据进行更新。然后视图会更新所有 {{twitterResult}} 实例,您会看到返回的数据。

我不明白的是,当对象在异步回调中更新时,作用域是如何知道值发生了变化的?

我不这么认为——回调必须在作用域上调用某种更新函数,对吧?但如果是这样,这是如何完成的?我听说过一个名为 $apply 的函数可能是答案 - 但是如何从资源内部引用 $scope.$apply()?

我创建了一个 JSFiddle 来说明问题:http://jsfiddle.net/Qcz5Y/10/

(编辑:这是一个较新的 JSFiddle,使用不同的异步调用来说明使用 ajax 调用而不是 setTimeout 的问题:http://jsfiddle.net/Qcz5Y/14/

在此示例中,您将看到一个控制器和一个资源。控制器的作用域有一个属性fruit,它一开始就设置为一个对象。当您单击“获取新水果”时,该属性设置为fruitResource.get(),它立即返回一个空对象。然后它进行异步调用,最后执行一个回调,更新最初返回的对象的值。

发生这种情况时,$scope.fruit 的值会正确更新为回调中设置的值。但它不会反映在视图中。

如果您单击“打印当前水果”,它会将$scope.fruit 的当前值记录到控制台。这只是为了检查;它具有更新视图的副作用。

我觉得我需要在get_callback 函数的底部执行类似 $scope.$apply() 的操作(您将在代码中看到它)。那是正确的做法吗?如果是这样,我如何在fruitResource 中获得对$scope 的引用?我知道我可能可以将$scope 作为参数传递给fruitResource.get(),然后以这种方式引用它,但是顶部提到的示例中的 $resource.get() 函数不需要这样做,所以我是希望 AngularJS 提供一些方法来自动从服务中获取范围。

谢谢!

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    我发现你可以将$rootScope 传递给服务,然后在上面调用$apply。所以当你想更新你的视图时,你只需$rootScope.$apply()

    这里是更新的 JSFiddle:http://jsfiddle.net/Qcz5Y/11/

    【讨论】:

    • 您自己的答案不正确:您需要使用 $rootScope.$apply 只是因为您使用的是 JavaScript setTimeout 方法而不是 AngularJS $tiemout 服务。
    • 您的更正不正确。请参阅我对您的解决方案的评论。我对其他解决方案的可能性持开放态度,但$timeout 不是答案。
    • $http 的行为类似于 $timeout (!),因此通过 $http(或 angular 提供的任何其他机制)进行异步调用将不需要 $apply。底线是这样的:从角度的“外部”进行调用时,您需要调用 $apply。
    • 同样,ajax 调用只是一个示例。我想到的真正用例是使用 indexeddb。据我所知,indexeddb 没有像 XHR 那样使用 $http 提供的包装器。所以是的,它会在角度之外,它需要$rootScope.$apply()。这是唯一通用的解决方案,这就是我正在寻找的。不过,我真的很感谢你的帮助!
    • 好的,不清楚您是否尝试在不使用角度服务的情况下进行通话。我只是想明确一点,调用 $apply 不是必须具有类似资源的行为。
    【解决方案2】:

    实际上模拟 Resource 的行为非常简单,归结为进行异步调用并立即返回空结果。然后,当数据确实从服务器到达时,需要更新返回的引用。这是使用您的用例的示例:

       get: function() {
            var fruit = {};
    
            // Asynchronous call that executes a callback. Simulation of ajax/db request            
            $timeout(function() {
                fruit.type = ['banana', 'strawberry', 'watermellon', 'grape'][Math.floor(Math.random() * 4)];
                fruit.count = [1, 2, 3, 4][Math.floor(Math.random() * 4)];
            }, 2000);
    
            return fruit;
        } 
    

    这里是完整的 jsFiddle:http://jsfiddle.net/pkozlowski_opensource/sBgAT/1/

    请注意,我正在使用 Angular 的 $timeout 服务来模拟异步调用,因为 $timeout 会在需要时调用 $apply,因此无需任何人工干预即可触发 UI 重新绘制。由于您尝试使用需要调用 $apply 的 setTimout,因此您遇到了问题。

    【讨论】:

    • 不,这是不正确的,因为您误解了问题。在我的 JSFiddle 中使用 setTimeout 只是模拟 ajax 调用之类的示例。 ajax 调用不会使用 setTimeout,因此不能简单地用 $timeout 替换它。我一直在寻找异步调用的通用解决方案,而不是专门的 setTimeout。所以使用$rootScope.$apply()是正确的,除非有更好的解决方案不使用$timeout
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-24
    • 1970-01-01
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 2018-12-28
    • 1970-01-01
    相关资源
    最近更新 更多