AngularJS ng-repeat与来自服务的数据

时间:2022-12-03 14:28:24

Originally in my app, I created controllers with very basic $http calls to get a resource by getting the ID of an object from the url ($routeParams). Ng-repeat display the results correctly.

最初在我的应用程序中,我创建了具有非常基本$ http调用的控制器,通过从url($ routeParams)获取对象的ID来获取资源。 Ng-repeat正确显示结果。

However, I noticed refreshing in a later view (different controller) wiped out the data and broke the page. So, I created a function on the service to be used in multiple controllers, to check whether the data has is available and to react as follows:

但是,我注意到在后来的视图中刷新(不同的控制器)消除了数据并打破了页面。因此,我在要在多个控制器中使用的服务上创建了一个函数,以检查数据是否可用并作出如下反应:

1) If the resource is defined, return it (no API call) 2) If the resource is not defined, get the id from the url and get it from the API 3) If the resource is not defined & you can't get the ID, just return false.

1)如果定义了资源,则返回它(没有API调用)2)如果未定义资源,则从URL获取id并从API获取它3)如果资源未定义且您无法获取ID,只返回false。

However, this broke the code: the template rendered before the service returned the data, and ng-repeat did not update. The code looks like this:

但是,这破坏了代码:在服务返回数据之前呈现的模板,并且ng-repeat没有更新。代码如下所示:

angular.module('myApp', ['ngCookies'])
    .config(...)
    .service('myService', ['$cookies', '$http', function($cookies, $http) {
        myData = {};

        return {
            getData:function(dataID) {
                if(myData.name) {return myData);
                else if (dataID && dataID !== '') {
                    $http.get('/api/data/' + dataID)
                        .success(function(data) {
                            myData = data.object;
                            $cookies.dataID = data.object.id;
                            return myData;
                        }
                }
                else { return false; }
            }
        }
    }]);

function myCtrl($scope, $http, $routeParams, myService) {
    $scope.data = myService.getData($routeParams.dataID);

    ...
}

And here's the template. It's in jade, which means rather than angle brackets, you just list the element with parameters in parenthesis right after, and content after the parenthesis.

这是模板。它是在玉中,这意味着而不是尖括号,你只需在括号中列出带有参数的元素,然后在括号后面列出内容。

h2 My heading
ul
    li(ng-repeat='option in data')
        a(href="#", ng-click='someFuncInCtrl(option.name)')  {{ option.name }}

When the controller did the $http.get itself, the ng-repeat worked fine because the $scope was updated in the ".success" callback. Now that there's a service that returns the data after a slight delay, "$scope.data" is just undefined, the ng-repeat list is empty.

当控制器执行$ http.get本身时,ng-repeat工作正常,因为$ scope已在“.success”回调中更新。现在有一个服务在稍微延迟后返回数据,“$ scope.data”只是未定义,ng-repeat列表为空。

I used a console.log to check myData right before return "return myData", and the myData is working, it just isn't returned in time, and for whatever reason the list is not updating whenever $scope does get the data.

我在返回“返回myData”之前使用了console.log检查myData,并且myData正在运行,它没有及时返回,并且无论何种原因,只要$ scope确实获取数据,列表就不会更新。

I looked a using $routeProvider's resolve... but that makes getting the ID from the url challenging, as the resolve object doesn't seem to have access to $routeParams. I know that $scope.$apply is supposed to help update the scope when it's altered by outside functions... but I have no clue where to put it. The most similar problem on SO didn't use a service.

我看了一个使用$ routeProvider的解决方案......但这使得从url获取ID具有挑战性,因为解析对象似乎无法访问$ routeParams。我知道$ scope。$ apply应该有助于在外部函数改变时更新范围......但我不知道在哪里放置它。 SO上最类似的问题没有使用服务。

I tried:

$scope.$apply($scope.data = myService.getData($routeParams.dataID));

And

$scope.$apply(function() {
    $scope.data = myService($routeParams.dataID);
});

Both times I only got Error: $digest already in progress.

两次我只得到错误:$ digest已在进行中。

1 个解决方案

#1


11  

The problem is on the way you interact with the service. Since your getData function can return both synchronous and/or asynchronous information, you can't just use normal return(s).

问题在于您与服务交互的方式。由于getData函数可以返回同步和/或异步信息,因此您不能只使用正常的返回。

$http.get('/api/data/' + dataID)
    .success(function(data) {
        myData = data.object;
        $cookies.dataID = data.object.id;
        return myData;
    });

The return on the above snippet will not return anything from getData because it will be executed on the context of the $http.get success callback (and not on the getData call stack).

上面代码片段的返回不会从getData返回任何内容,因为它将在$ http.get成功回调的上下文中执行(而不是在getData调用堆栈上)。

The best approach for handling sync and async service requests is to use promises.

处理同步和异步服务请求的最佳方法是使用promises。

Your getData function should look something like this:

你的getData函数看起来像这样:

getData:function(dataID) {
    var deferred = $q.defer();
    if(myData.name) {
       deferred.resolve(myData);
    } else if (dataID && dataID !== '') {
        $http.get('/api/data/' + dataID)
            .success(function(data) {
                 myData = data.object;
                 $cookies.dataID = data.object.id;
                 deferred.resolve(myData);
                 // update angular's scopes
                 $rootScope.$$phase || $rootScope.$apply();
             });
    } else { 
       deferred.reject();
    }

    return deferred.promise;
}

Note: You need to inject the $rootScope on your service.

注意:您需要在服务上注入$ rootScope。

And on your controller:

在你的控制器上:

function myCtrl($scope, $http, $routeParams, myService) {
    myService.getData($routeParams.dataID).then(function(data) {
        // request was successful
        $scope.data = data;        
    }, function() {
        // request failed (same as your 'return false')
        $scope.data = undefined;
    });
}

#1


11  

The problem is on the way you interact with the service. Since your getData function can return both synchronous and/or asynchronous information, you can't just use normal return(s).

问题在于您与服务交互的方式。由于getData函数可以返回同步和/或异步信息,因此您不能只使用正常的返回。

$http.get('/api/data/' + dataID)
    .success(function(data) {
        myData = data.object;
        $cookies.dataID = data.object.id;
        return myData;
    });

The return on the above snippet will not return anything from getData because it will be executed on the context of the $http.get success callback (and not on the getData call stack).

上面代码片段的返回不会从getData返回任何内容,因为它将在$ http.get成功回调的上下文中执行(而不是在getData调用堆栈上)。

The best approach for handling sync and async service requests is to use promises.

处理同步和异步服务请求的最佳方法是使用promises。

Your getData function should look something like this:

你的getData函数看起来像这样:

getData:function(dataID) {
    var deferred = $q.defer();
    if(myData.name) {
       deferred.resolve(myData);
    } else if (dataID && dataID !== '') {
        $http.get('/api/data/' + dataID)
            .success(function(data) {
                 myData = data.object;
                 $cookies.dataID = data.object.id;
                 deferred.resolve(myData);
                 // update angular's scopes
                 $rootScope.$$phase || $rootScope.$apply();
             });
    } else { 
       deferred.reject();
    }

    return deferred.promise;
}

Note: You need to inject the $rootScope on your service.

注意:您需要在服务上注入$ rootScope。

And on your controller:

在你的控制器上:

function myCtrl($scope, $http, $routeParams, myService) {
    myService.getData($routeParams.dataID).then(function(data) {
        // request was successful
        $scope.data = data;        
    }, function() {
        // request failed (same as your 'return false')
        $scope.data = undefined;
    });
}