I have created a factory which does a HTTP get to a php script.
The php script returns the proper data to the factory which then sends its data to the controller:
.controller('CatListController', ['$scope', 'Category', function ($scope, Category) {
$scope.categories = {};
var res = Category.list();
console.log(res); // <-- shows the proper object
console.log(res.data); //<-- shows nothing
if (res.error) {
$scope.error = true;
$scope.message = "http error";
}else{
if (res.data.error) {
$scope.error = true;
$scope.message = "database error";
}
$scope.categories = res.data;
}
}]);
the first console log displays the full object in its entirety, but the next console log display an empty object.
the object that is supposed to shown is (and does from the console log res):
{
data: { name: "" },
error: false
}
why is this happening?
I guess it is all right! Your first console.log prints an object. The browser keeps the output up to date with the state of the object. Keep in mind list() ist an asynchronous call, so the result will arrive later. The second console.log output prints a property of the object. The console did not update this if the data are arriving.
From the angular $ressource documentation:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
Related
I'm having a bit of trouble with my Angular factory. It returns data correctly
but in the chrome console when I try to log it, it's empty initially and chrome shows a message beside my object - object value at left was snapshotted when logged. When I expand my object everything is there. If I try to loop through the returned data, I get an undefined error.
I'm pretty sure this is promise territory, but I'm not sure how I would go about it. I just want to be able to loop through process the data returned from this factory in my controller. My code is below, any insights would be appreciated.
angular.module('myMap').factory('mapSorter', function($http) {
x2js = new X2JS(),
data = {
styles: [],
layers: []
};
return {
//pass in array of objects with url property
processMapData : function(kmls) {
//loop through each object
angular.forEach(kmls, function(kml) {
$http.get(kml.url).then(function(response) {
//push properties from response into data object
data.styles = data.styles.concat(x2js.xml_str2json(response.data).kml.Document.Style)
data.layers.push(x2js.xml_str2json(response.data).kml.Document.Placemark)
})
})
return(data);
}
}
});
Thank you!
I would consider having a look at $q.all. It accepts an array of promises and will resolve once all promises have resolved. You'll be able to do something like this:
$q.all([promise1, promise2, promise3]).then(function(values){
console.log(values[0]) // promise1 value
console.log(values[1]) // promise2 value
console.log(values[2]) // promise3 value
});
All you will have to do is push each promise into an array and then return $q.all(array). You'll then be able to call then within your controller and get all your responses.
Hopefully this helps
Let's say a service like this:
services.factory('User', function($resource){
return $resource('/rest/usersettings/:username', {}, {
get: {method: 'GET'},
update: {method: 'POST'}
});
});
So it is supposed to be used like this:
scope.user = User.get( {username: 'bob'} ); // GET
console.log( JSON.stringify(scope.user) ) // {"$promise":{},"$resolved":false}
So, when I send GET request, it goes OK, building this ur + params:
http://localhost:9000/rest/usersettings/bob
Question, why I have: {"$promise":{},"$resolved":false}
If my GET request leads to json-response back from the server:{"username":"bob","email":"bob#bobs.com"} then I'm expecting to have my scope.user filled by data.
Should I wait somehow promise is ready / resolved ?
User.get( {username: 'bob'} ) does not return your actual data immediately. It returns something will hold your data when the ajax returns. On that (the $promise), you can register an additional callback to log your data.
You can change your code to:
scope.user = User.get( {username: 'bob'} ); // GET
scope.user.$promise.then(function(data) {
console.log(data);
});
You will get your data in there, but not immediately.
Read the docs on ngResource:
It is important to realize that invoking a $resource object method
immediately returns an empty reference (object or array depending on
isArray). Once the data is returned from the server the existing
reference is populated with the actual data. This is a useful trick
since usually the resource is assigned to a model which is then
rendered by the view. Having an empty object results in no rendering,
once the data arrives from the server then the object is populated
with the data and the view automatically re-renders itself showing the
new data. This means that in most cases one never has to write a
callback function for the action methods.
For now I use this (it seems I duplicate this question )
User.get({
username: 'bob'
}, function(user) {
user.$update(
function(data, headers) {
console.log("GOOD");
},
function(err, headers) {
console.log("BAD");
}
);
});
This should work :
User.get( {username: 'bob'} ).$promise.then(function(data) {
scope.user = data.toJSON();
});
toJSON() cleans up Angular's internal properties ($$).
I've got an AngularJS resource implementation, which (in principle) works fine. Now the special circumstances are:
one of the fields (attributes) of the resource is the "last changed timestamp"
if it arrives at the server in an updating request, it is ignored. The sever sets the "last changed timestamp" always automatically
the updating methods on the server are all implemented in a way, that the response is empty (rather than containing the modified entity)
So under these circumstances, to get the "last changed timestamp" after an update operation from the server, I always have to do a get immediately following the update.
myEntity.$update( function(){ myEntity.$get(); } );
Now the question is: Does AngularJS offer a way to automatically chain actions:
to define in the MyEntity definition, that it needs to always do a $get after the $update.
and then, in application code just call
myEntitty.$update();
Thanks to #marck for pushing me in the right direction. The solution is basically
var MyEntity = $resource( ...,
{
get: {...},
update: {...}
}
);
// params is passed to $update.
// after successful $update, a parameterless $get is called
// success is called after successful $get
// error is called if $update OR $get failed
// returns the promise of the $update
MyEntity.prototype.$updateAndRefresh = function(params, success, error){
var item = this;
return item.$update(params, function(){
item.$get(success, error);
},
error);
}
I return a resource with a URL
$resource("http://foo.com/bar.json").get().
$promise.then(function(data){ $scope.result = data},
function(error){ $scope.msg = "error" } );
Resource returns
["item1"...."item_n",.....,"$promise", "$resolved", "$get", "$save", "$query", "$remove", "$delete"]
Why do I get all those objects in my data set. I'm guessing $promise just returns all this and waits for the server response. But once I have the server response where can I just get my server data without the Promise jargon?
If you look at the angular source here:
https://github.com/angular/angular.js/blob/master/src/ngResource/resource.js#L505
There is a toJSON method on the Resource prototype chain that will accomplish this for you.
For example:
$resource("http://foo.com/bar.json").get(function(res) {
$scope.result = res.toJSON();
});
You need to return wrapped result like {'result': { 'some_key': 'some_val' }} from your backend.
Or just do like described above.
Diary.getSharedWithMe(function(data) {
delete data.$promise;
delete data.$resolved;
_self.sharedDiariesWithMe = data;
}, function(error) {
console.log(error)
});
$resource returns an object or array that will have your data when the call completes. All those functions are there to help you out and $resource is mainly intended for CRUD operations. If you want the data, you have to wait for it to get returned so you might as well use the promise. If you want to strip all of those properties you can use angular.toJson to convert it to json, but angular does that for you when posting it back to a resource or $http call so you shouldn't have to.
$scope.data = $resource("http://foo.com/bar.json").get();
// $scope.data does not have your data yet, it will be
// populated with your data when the AJAX call completes
...
// later in a call from a save button maybe you can just do
// this to post your changes back:
$scope.data.$save();
So in case someone else is stumbling here and didn't understand promises/angularjs here is what is going on. When you use .then() or .get() you get a promise and some helper functions all in the same object. This is awesome because then you don't worry about callbacks being defined and whether data is available because the promise object always has some properties. This object contains your raw data in another object within. So the promise object is nested, you just have to reference the data object within when the data is ready.
Here's what I was doing
$resource("http://foo.com/bar.json").get().
$promise.then(function(data){ $scope.result = data},
//data is actually a promise object.
function(error){ $scope.msg = "error" } );
promise object
Note the data is actually under another object called "data". So in your success callback to get just the data you should do in this case: data.data
To automatically remove them from every request, you can add an interceptor:
angular.module('app').config(config);
config.$inject = ['$httpProvider'];
function config($httpProvider) {
$httpProvider.interceptors.push(interceptor);
}
interceptor.$inject = [];
function interceptor() {
return {
request: (config) => {
if (config.data) {
delete config.data.$promise;
delete config.data.$resolved;
}
return config;
}
};
}
I am trying to get data from json data so i write this:
app.factory('data', function($resource) {
return $resource('mock/plane_urls.json', {}, {
getTreeData: { method: 'GET', isArray: false }
})
})
and then using:
data.getTreeData.Carriers.Carrier;
Where Carriers is the first node in json file. but it doesnt work ;/ I got error
Error: data.getTreeData.Carriers is undefined
You are trying to reach into the Carriers object before it is fetched. Before the data comes back, Carriers is just an empty object.
You could do something like $scope.carriers = data.getTreeData(); and then in your view have an element with ng-repeat="carrier in carriers"
The idea behind $resource is that you get an empty object, so the view renders nothing. When the request is complete, the empty object is replaced and the view is re-rendered with the data automatically.
You could also use the $http service like so and provide a callback function that executes once the data is fetched.
$http.get('mock/plane_urls.json').then(function(data){
carrier = data.Carriers.Carrier
});
BUT! should break this into a service and a controller though. Just have the service getTreeData return the promise object like so: return $http.get('mock/plane_urls.json'); and then in your controller call it with then() and provide the callbacks for success and error:
getTreeData.then(
function(data){
carrier = data.Carriers.Carrier;
},
function(err){
console.log("Error: " + err);
});
You can use $scope.$watch to watch the value of the data.getTreeData.
When new data comes in, the callback will be called, then, you can get your data you want.
http://docs.angularjs.org/api/ng.$rootScope.Scope