Angularjs caching vs fetching live data - angularjs

I have a service I'm using to fetch data for an application.
Since a service is a singleton, I'd like to cache some reference data in the service, but I'm not sure how to get that to happen.
I'm using Strongloop and right now have a function in the Service like this:
function fetchReferenceData() {
return refInfo.find().$promise;
}
I'd like to have a property where I store the reference data.
I could add a property in the code, like
var myRefData;
and then I could edit fetchReferenceData() to check that property, but since the client is expecting a promise back, this won't work:
function fetchReferenceData() {
if (myRefData) { return myRefData; }
else { return refInfo.find().$promise;}
}
What's a good programming pattern to work with this kind of thing?
Do I pass in a function to call in a .then() to set the data in the client?

If the client is expecting a promise, you can use the $q service to create a promise from your cached value.
function fetchReferenceData() {
if (myRefData) { return $q.when(myRefData); }
else { return refInfo.find().$promise;}
}
The client can access it with the standard .then method.
fetchReferenceData().then (function (myRefData) {
//use myRefData
});
Best practices
You need a way to destroy the information in your cache.
You need to avoid calling refInfo.find() again if a query is already in progress.
Update for ngResource objects
Methods from Angular $resource services return references to empty objects.
From the Docs:
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.
So you can cache the query reference but you need to be aware the data on it will be undefined for a period of time.
The $resource service also adds a $promise property that can be used by clients that accept a promise.
function fetchReferenceData() {
var cache;
return cache = cache || refInfo.find();
}
clientThatNeedsPromise(fetchReferenceData().$promise);

Related

Sending a promise from one controller to another in Angular

In my web app, I would like to load all of the user data at the beginning, in the run method and pass the data to the other controllers to be shown to the user in the dashboard.
So lets say I have a data structure on the server
data = {
x : 30,
y: 20
}
I would like to get it in one http requests (instead of creating multiple http requests and creating multiple promises)
The http request I run in the run method of the app
service.getDataFromServer = function(){
return $http.get('/admin/data').then(function(response) {
data = response.data;
});
};
This will create a promise.
I would like to take this promise and pass it to other controllers to be used.
My first question is how can I do it so the data from the promise will bind to the data I am about to show to the user.
The second question is, can I use the structure fields before the promise is even resolved?
for example
$scope.x = data.x
And by data I mean the data which is about to be resolved
Thanks
I would like to get it in one http requests (instead of creating multiple http requests and creating multiple promises)
After getting response from http call,save the data in factory/services so,you can use is at another location like controller in your case by injecting it which is most recommended.
I would like to take this promise and pass it to other controllers to be used.
how can I do it so the data from the promise will bind to the data I am about to show to the user.
make http call and save the promise.
service.getDataFromServer = function() {
var promise = $http.get('test.json');
service.savePromise(promise);
}
and
get saved promise.
.controller('testCtrl', function(service) {
service.getSavedPromise().then(function(response){
console.log(response.data)
})
})
Plunker here
The second question is, can I use the structure fields before the promise is even resolved? for example
$scope.x = data.x
// data is still undefined as promise is not resolved.so,you will get TypeError
What you can do best here,you can merge data to your existing data object/array,so,Angular two way binding can do rest of the things for you.

Remove $promise and $resolved from json object not working

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.
});

How to set an object to the updated value returned by Angular $resource?

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) {});

angular resource clears object data after post

my angular app clears data after post.
Here is the snippet from controller:
$scope.saveDevice = function() {
var deviceId = $scope.device.id;
if (deviceId) {
$scope.device.$update(function(result) {
$location.path('/devices');
});
} else {
$scope.device.$save().then(function (deviceResult) {
$scope.device.id = deviceResult.deviceId;
$scope.device.$activationCode(function (result) {
$scope.device.activationCode = result.activationCode;
});
});
}
};
When I hit break point at
"$scope.device.$save().then(function (deviceResult) {" the application shows that device is populated with properties from form. But after the post, device is cleared of any properties. Is this normal behaviour? If so, how can I prevent it?
Here I found the answer to my problem:
AngularJS - Prevent Form Clear
Basically:
call class method
Device.save($scope.device) //....
instead of
$scope.device.$save
and it will presist the data you've in $scope.device class.
I'm not sure if this helps, but from the docs. This is too long to put as a comment.
Angular Doc
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.

Why doesn't UnderscoreJS's _.extend() method copy AngularJS promises to the extended object?

Example:
$scope.post = Posts.get({id: id});
scope = _.extend({}, $scope);
alert($scope.post.id); // undefined
alert(scope.post.id); // exception - post is not defined
I feel that I'm doing something obviously wrong, but I can't figure out what. I expected it to shallowly copy the post reference to the new object:
alert($scope.post.id); // undefined
alert(scope.post.id); // undefined
$scope.post === scope.post; // true
This is from Angular documentation about 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.
At the time when you are trying to extend the scope, your object is not fully there.
I hope that helps...

Resources