问题
I am assigning two collections into my scope in a controller; both come from services wrapping RestAngular and return promises. Each individually returns the collection I want, but when I assign both to $scope one of them never resolves (it's the same one that fails, no matter the order they're in).
If I log to console by calling the offending service again, then lo and behold the original assignment works perfectly.
The services both work pretty much the same:
.factory('Role', ['Restangular', '$q', function(Restangular, $q){
var _collection = [];
var _roleService = Restangular.all('roles');
return {
getList: function() {
// return _roleService.getList();
var listDeferred = $q.defer();
_roleService.getList()
.then(function(list) {
listDeferred.resolve(list);
_collection = list;
});
return listDeferred.promise;
}
}
}
])
and the controller is specified as follows (I've left the console.log in, but removing it causes $scope.roles to never resolve):
.controller('ResourceCtrl', ['$scope', 'Resource', 'Role', function($scope, Resource, Role) {
$scope.roles = Role.getList();
$scope.resources = Resource.getList();
console.log(Role.getList());
}])
Everything is done within Angular so I don't think I'm missing an $apply - but unless I've stumbled on a bug there must be something wrong with my code...
EDIT
I found out what's causing the error, but don't have a solution. My Resource service is very similar to the Role one, but because Resources have Roles, I inject Role into Resource and use it to link individual elements together. Once I deleted that line all the scope / promise stuff came back.
Which I guess takes me back to: how do I link services together reliably (i.e. so Resources with role fields can have the corresponding Role objects attached to them at the service level).
Here's my Resource:
.factory('Resource', ['Restangular', '$q', 'Role',
function(Restangular, $q, Role){
var _resourceService = RestAngular.all('resources');
var _roleService = Role;
var _convertObjectsToUrls = function(item) {
for (var property in item) {
if (item.hasOwnProperty(property) && typeof(item[property]) == 'object' && item[property] != null) {
item[property] = item[property].url;
}
}
return item;
}
var _convertUrlsToObjects = function(item) {
for (var property in item) {
if (item.hasOwnProperty(property) && typeof(item[property]) == 'string' && item[property] != '' && property != 'url' && item[property].substr(0,4) == 'http') {
/* THIS LINE BREAKS IT */ item[property] = _roleService.getByUrl(item[property]);
}
}
return item;
}
var _getIdFromUrl = function(url) {
var pathElements = url.split('/')
return pathElements[pathElements.length - 2]
}
var _cleanParams = function(item) {
for (var property in item) {
if (item.hasOwnProperty(property)) {
item[property] = undefined;
}
}
return item;
}
return {
add: function(item) {
var responseDeferred = $q.defer();
item = _convertObjectsToUrls(item);
_resourceService.post(item)
.then(function(response){
response = _convertUrlsToObjects(response);
response.name = response.first_name + ' ' + response.last_name;
_collection.push(response);
responseDeferred.resolve(response);
item = _cleanParams(item);
});
return responseDeferred.promise;
},
edit: function(item) {
var responseDeferred = $q.defer();
var idx = _collection.indexOf(item);
item = _convertObjectsToUrls(item);
item.customPUT(_getIdFromUrl(item.url))
.then(function(response){
response = _convertUrlsToObjects(response);
response.name = response.first_name + ' ' + response.last_name;
_collection.splice(idx, 1, response)
responseDeferred.resolve(response);
});
return responseDeferred.promise;
},
delete: function(item) {
var responseDeferred = $q.defer();
var idx = _collection.indexOf(item);
// item = _convertObjectsToUrls(item);
item.customDELETE(_getIdFromUrl(item.url), {})
.then(function(response){
response = _convertUrlsToObjects(response);
_collection.splice(idx, 1)
responseDeferred.resolve(response);
});
return responseDeferred.promise;
},
getList: function() {
var listDeferred = $q.defer();
var list = _resourceService.getList()
.then(function(list) {
_.each(list, function(item, index, list){
item = _convertUrlsToObjects(item);
item.name = item.first_name + ' ' + item.last_name;
})
listDeferred.resolve(list);
_collection = list;
});
return listDeferred.promise;
}
}
}
])
回答1:
Not sure from where the Resource service comes and whether is is of importance. Since you return a promise in your Role service, you can just do:
.controller('ResourceCtrl', ['$scope', 'Role', function($scope, Role) {
Role.getList().then(function(roles){
$scope.roles = roles;
});
}])
Alternatively, you can let Angular resolve the role for you (if you are just using the roles in a template, for example)
.controller('ResourceCtrl', ['$scope', 'Role', function($scope, Role) {
$scope.roles = Role.getList();
}])
Edit: I also noticed the var _collection = [];
which seems not needed. Maybe you can also post the code for the Resource service?
This code of your Role service should be sufficient.
.factory('Role', ['Restangular', '$q', function(Restangular, $q){
return {
getList: function() {
var listDeferred = $q.defer();
Restangular.all('roles').getList().then(function(list) {
listDeferred.resolve(list);
});
return listDeferred.promise;
}
}
}
])
Even further edit:
This plunkr shows that in angular 1.2rc3 the first method works: http://embed.plnkr.co/SU5UMK7jNffXWnWiV1HE/preview
But if you were to try to take advantage of the automatic unrwapping of promises (which is deprecated in rc3, you are out of luck): http://embed.plnkr.co/zdepkLCesYkj8pXYFTN2/preview
来源:https://stackoverflow.com/questions/19798586/angularjs-multiple-promises-resolving-erratically