I'm getting data from the server using $resource like this
service
.factory('rulesService', ['$resource', function ($resource) {
var systems = $resource('url');
return systems;
}]);
controller
$scope.rules= rulesService.query();
console.log($scope.rules);
The output I get is
0: Resource
1: Resource
$promise: Promise
$resolved: true
length: 2
I tried to strip $promise & $resolved using
1) angular.toJson($scope.rules)
2)JSON.stringify($scope.rules, null, 2)
Both these are returning []
Can someone help me on this
After reading your comments above, I think your problem is you use $resource wrong.
$resource will return a empty object or array, and then make the http call in the background and populate the array/object once it is complete.
This means that, at the time you console.log the object, it is actually an empty array, but since the log in the browser is pretty smart, it will update the object in the log as well, once the $resource call is done.
this is why console.log(rules[0]) is undefined, while console.log(rules) says the element exists. It didn't at the time of the log.
If you need to do further processing you have to do something like:
var rules = rulesService.query(function () {
console.log(rules[0])
})
you can also use promises instead of a callback, but either way you need to ensure the data is fully loaded
You should be able to simply iterate over the array (and ignore the extra properties). If you want a clean array you could always use a map or similar.
$scope.new_rules = $scope.rules.map(function (rule){
return rule;
})
Your problem is related to asynchronous execution and race conditions.
You're trying to refer to rules[0] before data actually arrives from the $resource.query() call.
Examples:
var rules = rulesService.query();
console.log(rules[0]); //will print nothing. the GET request hasn't been resolved yet.
rules.$promise.then(function (response) {
console.log(rules[0]); //This WILL work assuming you actually get data from the backend.
console.log(response[0]); //This will also work with the same data.
});
Related
I have a value in my view that is not updating after a service method is called.
Here is the relevant code in the controller:
$scope.remaining = 20;
AcctService.getCurrentCount.get(calculateRemaining); //This is a $resource method
function calculateRemaining(result) {
$scope.remaining -= result;
alert($scope.remaining);
}
Here is the code for .getCurrentCount:
service.getCurrentCount = $resource('/api/getCount', {}, {
'get': { method: 'GET', isArray: true }
});
With the above code, say for example the result returned is 5. "15" will be alerted. However, in the view, {{remaining}} is still 20. No errors, the view just doesn't update.
I have tried the following:
$timeout - nothing different happens
Making $scope.remaining an object with property "value". (I read in another post about issues with data binding of primitives vs references). No difference.
$promise and .then() - no difference
$apply results in a digest error
Note, I am also coding with Ionic, not sure if it makes a difference. I disabled caching in the Ionic config, and another service method that returns an array propagates an ng-repeat as expected.
Thanks!
I'm not sure what things look like inside that get() function, but it doesn't look right.
Assuming get() returns a promise, you should write it like this:
AcctService.getCurrentCount.get().then(calculateRemaining); //This is a $resource method
First of all you do not need to create get method to return array. Use default 'query' method of $resource. And first parameter for the method is an object of parameters. second one is success function. So change you service to this
service.getCurrentCount = $resource('/api/getCount');
And later use it as
AcctService.getCurrentCount.query({},calculateRemaining);
Also check if you are not using one way data binding {{::remaining}}
And also you have to make sure you are using right $scope, to check that make "remaining" a field of an object. You can do it this way:
$scope.myData = {};
$scope.myData.remaining = 20;
and later at the controller initialize it the same and at the html
{{myData.remaining}}
also you can use $scope.apply(); but actually that is used at different case
How do you save an object to a $resource and then set the object to the API response value returned by the $resource?
I have an object, value (which is part of an array of objects I am iterating through), that I save to a ReST API.
When the save is successful, the API responds with same object, plus any extra details such as the object's id (if the save was a create) or perhaps the file paths that the back end saved to. Stuff the front end can't know about until the back end adds it.
I want to update the saved value object with those extra details returned in the response, but I can't figure out how to do it.
I have tried returning the response from the $resource callbacks, but I can't make it work. It returns a $$state object (which I don't know what to do with) rather than the response. I may be doing that wrong.
In any case I would rather set the value object to the response object inside the success callback, because I only want to set the object to the response when the save is successful, not when there's an error.
But I don't know how to pass the value object to the callback to be updated, which is the crux of my problem.
(And I can't figure out how search the array for the object from inside the callback because the $resource success response doesn't include the $$hashkey. Only the error response includes the $$hashkey. This approach seems like a kludge to me anyway)
Here is the basic pattern I am trying to use. I'm hoping someone will point out the flaw with the pattern, or show me how to pass value to the callbacks, or whatever. Ideally I'd like to know what the best practice pattern is for saving objects to a $resource and immediately updating them with the response data.
(Resource is the name I have given in this example to an Angular factory that returns a $resource object.)
function success(response) {
//I want to set value to response here, but value is out of scope
//so instead I do this but it returns $$state rather than response
return response;
}
function error(response) {
Status.reportError(response);
}
value = Resource.create(value)
.$promise.then(success, error);
I imagine I could find value somewhere in the $scope of the callback (?) but I'm trying avoid using $scope due to its pending deprecation.
Thanks in advance
Jay Dee
Should be like this:
Resource.create(value, function(success) {
value.id = success.id;//depends on what you return, may be success.data.id or similar
}, function(error) {});
So I wanted to execute 2 http calls to get some groups and questions from my server and have both of these resolved in routeprovider so that the corresponding controller will not load before I have the relevant data.
In my other controllers I always worked with an initalData object to hold the data.
The resolve part:
resolve: {
initialData: ["GroupsService" "QuestionsService", function (GroupsService, QuestionsService) {
return {
questions: QuestionsService.getAll(),
groups: GroupsService.getAll()
}]
}
When I tried to access the data in the controller using initialData.questions and initialData.groups respectively, I however received 2 promises instead of the data, even though the callback from the http got logged before the controller was instantiated.
QuestionsCtrl.$inect = ["DialogsService", "initialData", "QuestionsService"];
function QuestionsCtrl(DialogsService, questions, groups, QuestionsService) {
//Initialdata object which has 2 Promise properties
//This logs AFTER the callback in both http callbacks
console.log('controller initialized', initialData);
When I replaced the code with this (didn't use an initialData object, instead returned two other objects, it did work:
resolve: {
questions: function (QuestionsService) {
//$http call for all questions
return QuestionsService.getAll();
},
groups: function (GroupsService) {
//$http call for all groups
return GroupsService.getAll();
}
}
Does anyone have any logical explanation for why, in the first case I got back promises (despite that the data was actually present in the client), and the second one worked flawlessly?
When you pass resolve to a route it calls $q.all on it implicitly for you. So when you return multiple values in resolve it waits for all of them to finish.
In your example - you just returned an object containing some values that are promises - you didn't wait for them so it got resolved immediately with the promises instead of unwrapping them.
You can of course explicitly wait for them too:
initialData: ["a" "b","$q", function (a, b, $q) {
return $q.all({
questions: a.getAll(),
groups: b.getAll()
});
}]
If you wish resolve to wait, you need to return a promise, in your first case it's not a promise, rather it's just an object, it happens to have 2 objects that are however promises, but angular won't know that... If you wish to return it as a single resolve you can use:
return $q.all({ key1: promise, key2: promise });
and add $q as a dependency
Another thing, promises doesn't turn them self into raw values when data is received from the server, they stay promises, in the case of resolve, angular will dig out the resolved value and provide those instead of the promises. And again we need to go back to that angular needs to know it is dealing with promises.
i'm using angularJS and SLIM PHP restful server, the PHP service is working and actually i have already used $http.get() with no problems in this application ...
But now a strange thing is happening, i created a new function in the same way that the others, and it get .success(function(data)) with no problems, i actually can console.log(data) and it shows the right results, but when .success() finish and return, i recieve a undefined result.
ps: there is no error in browser console.
var markerOptions = [];
loadMarkers();
console.log(markerOptions);
function loadMarkers() {
$http.get('http://localhost/rest/getMarkers').success(function(response){
console.log(response);
markerOptions = response;
});
}
Console.log() inside success() return the right data
Console.log() after loadMarkers() return undefined
#MarcKline's comments are correct. Anyways, following what I think you're trying to achive by this piece of code of yours, you can assign the returned data from the ajax response to a scope variable (assuming you're using $scope), e.g $scope.markerOptions = response. You can declare markOptions as a scope variable by var $scope.markOptions = [] (...and, of course, log it by console.log($scope.markOptions) accordingly). Also, define $scope.loadMarkers = function() {...} and call it by $scope.loadMarkers()
The scope will be updated as soon as the client-side gets its ajax response.
Hope it helps your current needs in addition to a better understanding of javasciprt's async approach that some of its principles were explained to you by the comments.
I found this snippet of code that does what I want it to:
var promise = this.model.save();
$.when(promise).then(function() {
console.log(promise.responseText);
});
I want to get back the responseText from my Backbone call to this.model.save(). This code was documented here. But it's not logging anything, even if I pull a raw text string in the console.log() call.
Could someone please explain in layman's terms what a jQuery promise is? I've read about them, but I don't think I quite got what they were. That might help me understand why this code isn't working for me. If I console.log(promise) in between the first and second lines of code, then I get the responseText. So something is happening in either the $.when or the then that is causing this to go wrong.
EDIT:
After reading the article, I discovered I could do this:
var promise = this.model.save();
$.when(promise).then(null, function(obj) {
console.log(obj.responseText);
});
But I don't understand what the null represents. then seems to take two parameters, a success function and a failure function. But wouldn't the success function be first? I get a 200 response from the server.
So first off, I'm pretty sure you don't need the when part; from the jQuery docs:
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the
Promise interface, giving them all the properties, methods, and
behavior of a Promise (see Deferred object for more information).
Since Promise has a then method already, you can just do:
this.model.save().then(null, function(obj) {
console.log(obj.responseText);
});
(The fact that the above code almost reads like an English sentence is a major advantage of using Deferreds, for me at least.)
As for your null argument, the docs are again pretty clear. There are three signatures for then (and that's just to cover the different jQuery versions; any given version has less):
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
deferred.then( doneCallbacks, failCallbacks )
deferred.then( doneCallbacks, failCallbacks [, progressCallbacks ] )
As you can see, all three take the "done" function first, and the failure function second. This does seem to imply that you're getting a failure, which is confusing. One way to avoid the problem is to not use then at all. Instead, try the following:
this.model.save().always(function(obj) {
console.log(obj.responseText);
});
That will make your function get called no matter what happens. However, you probably should figure out what's going on, so you might want to instead add a success and failure callback to do some debugging:
this.model.save().done(function() {
// Success case
}).fail(function() {
// Failure case
});
Because this.model.save returns a promise, you can do the following instead:
this.model.save()
.done(function(response) {
console.log("Success!");
})
.fail(function(response) {
console.log("Error!");
});
(That's easier than the whole $.when bit.)
My guess is that although your response is returning a 200 code, it is still "failing" because the response data type doesn't match up with what you're expecting (what's set in the dataType attribute in the $.ajax call).
I am a big fan of using promise, and I think the promise means very similar things in different packages.
To answer your question, which previous answers didn't, the "then" function is a function of a promise, the "when" function is a fail-save, incase the object is not a promise, a "when(obj)" will make sure it is a promise, so that you can use the elegant xxx.then(success(){},error(){}).
btw, the "deferred" that machineghost said is the package that let you use promise.
For starting to know promise and how to use it. check out this tutorial. It explains every thing very clearly, it is the article that made me so into promises.
http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
Now, as machineghost said, it seems your sync call is getting an error, according to a REST API documentation,https://parse.com/docs/rest# (don't know if it is the same as backbone), the server will response a JSON object for a "create" request in this format:
{"createdAt": "2011-08-20T02:06:57.931Z","objectId": "Ed1nuqPvcm"}
My guess is, maybe your server did not respond the request with the correct JSON, so the save() think the operation failed because there was no "createAt" field, event thought your sever did created the item.