如何在AngularJS中返回时获取异步服务调用以更新视图?

时间:2012-08-19 01:17:57

标签: javascript angularjs

这是对我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。这只是为了检查;它有更新视图的副作用。

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

谢谢!

2 个答案:

答案 0 :(得分:9)

我发现您可以将$rootScope传递到服务中,然后在其上调用$apply。因此,当您想要更新视图时,只需执行$rootScope.$apply()

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

答案 1 :(得分:1)

实际上,模拟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,因此您遇到了问题。