Use dynamic parameters in Angular Resource - angularjs

the Angular-Resource documentation says for parameters:
If any of the parameter value is a function, it will be executed every time when a param value needs to be obtained for a request (unless the param was overridden).
Unfortunately this does not work for me, the function is only called once when the resource is initialized.
My case:
I have a global Application state which you can choose on the top navigation of the app. This state affects a query param in most calls. (e.g. the date for which the call is valid). I maintain this global state in a service.
My Services (model layer) look like this:
function($resource, ApplicationState) {
return $resource('test/:_id', {
date: ApplicationState.getCurrentTime()
}, {
get: {
method: 'GET'
}
});
}
I'd expect ApplicationState.getCurrentTime() to be called on every get call. It is only called once.
I created a plunkr as an example. Open the Browsers Debug Console to see that ApplicationState.getCurrentTime() is only called once and not every time you click on the button.
http://plnkr.co/edit/qeoIUuzFCqnQNdnYp5ZX?p=preview
What do I do wrong? Is there a better way to inject dynamic parameters to some resources? I know the concept of request intereceptors, but I need this only on some services and not all.
Thanks for your help!

I found the answer with the help of frfancha on Github, the trick is to pass the function itself and not the result of the function.
See the missing brackets after ApplicationState.getCurrentTime here:
function($resource, ApplicationState) {
return $resource('test/:_id', {
date: ApplicationState.getCurrentTime
}, {
get: {
method: 'GET'
}
});
}
The function is now evaluated every time a request is made.
An updated Plnkr can be found here: http://plnkr.co/edit/Qx9HSsFwt5LDiM6Vmgff?p=preview
Credits go to frfrancha on Github

Related

Angular default $http cache removes after route change

I am using Angular's default $http cache in one of my services. When the user navigates from a view to another one (I am using ui-router), the cache invalidates and all of the items will be removed from it. I want to not invalidate the cache in the whole lifetime of my application.
EDIT:
For example, this factory does not return cached result after navigating to another route and it calls the server api to get the result:
cardModule.factory("myFactory", function ($http) {
return {
getAll: function () {
return $http.get("all", { cache: true })
}
}
});
How to prevent default cache from removing items from itself after a route change?
I found the source of the problem. It was my own fault. I had a code somewhere that clears the cache after the state change. There is no problem with default angular $http cache.
I would leave this as a comment but I don't have enough points yet..
Could you try some form of memoisation? In other words, have a model on the scope, then, if the model is undefined, trigger the $http call? Something like:
var ctrl = this;
ctrl.product = undefined; // or null
if (ctrl.product === undefined) { // or null
$http.get(...).then(function(resp) {
ctrl.product = resp.data;
};
};
This way the model gets initialized, and called just once. A possible downside would be that the if statement may make this inefficient.
I have not tested this, just throwing the idea out there. I am also very interested in this problem.
That should not be related to ui-router or $http. Here are a few things you need to confirm:
Is your server (which is serving your resources) is setting the cache header or not
Make sure you are not using Ctrl + F5 to refresh the page
If you are using Chrome browser, make sure a setting Disable cache is unchecked

Play 2.3/Angular JS $resource/routing issue

So I'm trying to AJAX a single solr doc from my results list to a "doc view" view. I'm trying to use AngularJS to AJAX to my view render method and display the doc that way, but I can't seem to get the angular to work and I'm not sure I'm doing things correctly on the Play side either. Would you at least be willing to tell me if what I'm trying to do will work? The Angular error comes from the docText.text(); call. Here is my code:
Angular controller code:
var docText = $resource("http://localhost:9000/views/full-doc-text.html", {
text: {method: 'PUT'}
});
$scope.handleViewText = function(value) {
docText.text({doc: value});
}
Java code:
public static Result viewText() {
JsonNode json = request().body().asJson();
//do stuff here
return ok(viewtext.render(json));
}
route:
GET /views/full-doc-text.html controllers.Application.viewText()
I see three problems with the code above;
1.The definition of docText resource is not correct. if your read the angularjs manual here you'll see that $resource has 4 parameters. First one is resource url, second is parameter defaults, third one is custom actions and forth one is resource options where last three of them are optional. In your code you pass custom actions as the second parameter, which should be the third. And since you don't have any parameters in your resource url second parameter must be null. So first correction is:
var docText = $resource("http://localhost:9000/views/full-doc-text.html", null, {
text: {method: 'PUT'}
});
2.You define your text action's HTTP method as PUT however in your routes file you are handling GET requests for your desired action. You should change your route definition as:
PUT /views/full-doc-text.html controllers.Application.viewText()
3.PUT method is usually used for update operations when implementing a RESTFULL service. In your case you don't seem to be updating anything. So I suggest to use POST method just for convention.

How do you update a cached resource in an angular service?

What is a good pattern for updating angular data from a ngResource service that has been cached?
I been trawling posts like this one [1]: How to refresh / invalidate $resource cache in AngularJS, but would be good to hear from angular experts on the right approach for this specific (but pretty general) scenario.
I am looking for a general pattern here. Both in understanding and in implementing angular - I am a novice at it.
I have a pretty standard ngResource service that has a very standard query method, and a custom put method update.
myServices.factory('ThingsService', [
'$resource',
function ($resource) {
return $resource('/api/things/:id', { id: '#id' }, {
query: { method: 'GET', isArray: true, cache: true },
update: {method: 'PUT', cache: true },
});
}]);
I am using it from a controller like this:
$scope.things = ThingsService.query(function (x) {
// must assign these only once data is loaded
$scope.allCount = x.Things.length;
$scope.incomingCount = $filter('filter')(x.Things, { State: 'incoming' }).length;
});
So far so good. The data is returned just fine, and it renders nicely in a dashboard view.
We support in-place-editing and the user can edit the data right there in the dashboard list.
First take a shadow copy of the thing using angular.copy(...) so that we can support buffering of the changes for the user. (just like a dialog box does for a user). Then when they confirm their changes, we call with the shadow copy:
ThingsService.update({ id: currentThing.Id }, { Data: currentThing.Data }, function () {
//TODO: now, if this PUT succeeds,
//I want to update the value of $scope.things array to reflect the changes,
//without going to back to the server for the whole array.
});
This correctly PUTS the changes to the server, which returns an updated thing, but the dashboard view which is bound to the query method is not updated auto-magically. Was kind of hoping angular and the ThingsService and its cache would take care of that for me somehow, you know by updating the cached data. Since the service should know that I just updated one of the items that the service serves up.
So to avoid going all the way back to the server we have told the ThingsService to cache its results, which is a good start. But how are you supposed to update the changed thing in the cached data?
Is there a standard pattern for this kind of update with a ngResource service?
Preferably I wouldn't have to mess with the cache directly. I should not even care that it is cached or how. I just want $scope.things to reflect the posted changes changes.

Use return data from an angular service resource across all controllers with only one call

I have a custom Web API written in .NET that returns user's information that will be used in my AngularJS application. I want to be able to call my API once, and then use that returned JSON across my entire AngularJS application without having to recall the API within each of my controllers.
I currently have a factory of services, one of which returns all of the client's details I need to use the in rest of the services.
.factory('customApiService', function ($resource) {
return {
userInfo: function(userId, callback){
var api = $resource('../../api/UserInfo/:userId', {
userId: userId
}, {
fetch: 'JSONP',
'query': { isArray: false }
});
api.fetch(function (response) {
callback(response);
});
}
}
)
Now I don't want to call this userInfo service in every controller, but I do want the data to be passed into each without calling my API multiple times.
customApiService.userInfo(userId, function (d) {
var gaProfileId = d.gaProfileId;
var yelpId = d.yelpId;
var tripAdvisorId = d.tripAdvisorId;
var facebookPageName = d.facebookPage;
var twitterHandle = d.twitterHandle;
var clientName = d.clientName;
var searchlightAccountId = d.searchlightAccountId;
var searchlightDomain = d.searchlightDomainId;
}
You can try global variables .
use a $rootScope https://docs.angularjs.org/guide/scope
$rootScope is available in all controllers an templates .Just inject $rootscope in your controller or wherever required.
From what I read of your description and responses to other questions, it sounds like you're trying to make an asynchronous call before the rest of your app starts up. This is possible, but complex, and sort of voids the point of Angular in the first place. As I see it, you have two options:
QUICK HACK: If you really want this kind of behavior, why start your app at all? Do your request first, before you define your app in the first place, then define your app in the result handler for the request.
RIGHT WAY: Alter the behavior of your services and controllers to tolerate not having enough information to fully start. A lot of times this is less difficult than it sounds. Usually you can just chain a promise into their initialization block of code to wait for the data you need. Take a look at Brian Ford's "Angular Modal" project, at the lines of code I've highlighted here:
https://github.com/btford/angular-modal/blob/master/modal.js#L25-L36
This technique sets up a promise to return from the function. If the data it needs is already loaded from the service, it resolves the promise immediately. Otherwise, it makes the call to get what it's after, and you can see later (line 39) that the module uses promise.then() to wait until it has the data it needs to run.
This is a great pattern for both controllers and services when working with asynchronous data.
If using a $resource call instead, note that most $resource calls return a promise in a property called $promise. You can do something like this:
var MyController = function($scope) {
// This will get set soon!
$scope.myData = null;
var myResource = $resource('/path/to/:someId', { someId: '#id' });
myResource.get({ someId: 1 }).$promise.then(function(data) {
$scope.myData = data;
});
};
You can do more things in the .then() resolution callback for the promise, like initialize more parts of your controller, etc. There are also ways you can delay starting your entire controller until the resource is available. One really cool way is if you happen to be using the Angular ui-router module, there is a "resolve" option when defining each route. You can use that to call the $resource as shown above, and ui-router will wait to start your controller/view until it has what it needs.

Change data between controllers with service

When I use this it works:`
angular.module('app').service('DataService', function() {
return {theme: "amelia"}
});
But when I use this, there is no update? Can you tell me the difference?
angular.module('app').service('DataService', function() {
return {
theme: function() {
return {theme: "amelia"}
}
};
});
Controller
$scope.settings = DataService.theme();
Jade
select.form-control(ng-model="settings.theme", ng-options="theme for theme in themes")
Is it possible to get the second way working? Because I will share more data then one Object!
Thank you!
The first version of the code calls the function once to instantiate the service. After that, because services are singletons in angular the function isn't called again, but rather the return value (a "static" object) is accessed in every controller that uses the service after that.
The second version, each controller you inject the service into calls the theme function, which instantiates a brand new object each time. You have now effectively mitigated the fact that the service is a singleton. This is why data will not be shared with the second set of code.
If you put a break point on the function call in each case and run your code you should see the first version called once while the second version will be called many times.
"Get It Working"...
You can't really make it work with a function call but if you need to share multiple data objects there isn't any reason not to nest them. You could very easily do something like:
angular.module('app').service('DataService', function() {
return {
dataObjects: [
{"type":"theme", "theme":"amelia"},
{"type":"user", "id":123, "name":"ABC"}
]};
});
In the example I added a second object which is a user object to make shared "dataObjects" array. To find a specific object in the "dataObjects" array, you could loop till you find the correct type ("theme", for example). If necessary, you could even nest one level deeper if you needed the objects to be pristine (without the added type attribute).
Hope that helps!
It should be theme: function().... inside your service. Replace "=" with ":".

Resources