I've correctly set Restangular to cache my http requests via:
Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setDefaultHttpFields({cache: true});
});
I would like, however, to be able to manually certain cached elements at a given point in time, eg when they become obsolete due to the user modifying these objects. Is there a way to do this? I've tried:
$cacheFactory.get('$http').info()
Object {id: "$http", size: 7}
Just make use of the $cacheFactory API as documented here:
https://docs.angularjs.org/api/ng/service/$cacheFactory
For instance, to invalidate a given cache entry:
$cacheFactory.get('$http').remove(myGetUrlToInvalidate);
myGetUrlToInvalidate is a string representation of your GET request URL. You may have to test to find out whether its the
relative or absolute URL, check out following stackoverflow discussion for more details:
How to refresh / invalidate $resource cache in AngularJS
Also of interest this stackoverflow discussion pointing to an alternate cache implementation:
Is there a way to get all of the keys out of a cache in Angular, specifically the template cache?
Restangular resources:
https://github.com/mgonto/restangular#can-i-cache-requests
https://github.com/mgonto/restangular#setdefaulthttpfields
Related
I havent find ressources online to solve my problem.
I'm creating an app with React Native that fetches and shows news articles from my database.
At the top of the page, there's some buttons with filters inside, for example:
one button "energy",
one button "politics"
one button "people"
one button "china"
etc...
Everytime I press one of those buttons, the filter corresponding is stored in an array "selectedFilters", and I want to fetch my database to only show articles that are corresponding to those filters.
Multiple filters can be selected at the same time.
I know one way of doing it, with a POST request:
await fetch('187.345.32.33:3000/fetch-articles', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: 'filters=${JSON.stringify(selectedFilters)}'
});
But the fact is, I read everywhere, and I also was teached, that POST request are used when creating or removing, and theoretically, what I should use is a GET request.
But I don't know how to send an Array with GET request.
I read online that I can pass multiple parameters to my url(for example: arr[0]=selectedFilters[0]&arr[1]=... but the fact is I never know in advance how many items will be in my array.
And also I'm not sure if I could write exactly the same way as my POST request above, but with GET:
await fetch('187.345.32.33:3000/fetch-articles', {
method: 'GET',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: 'filters=${JSON.stringify(selectedFilters)}'
});
or if I can only pass items in the url, but does this work ?
await fetch('187.345.32.33:3000/fetch-articles?arr[0]=${selectedFilters[0]', {
Or even better if something like this could work:
await fetch('187.345.32.33:3000/fetch-articles?filters=${JSON.stringify(selectedFilters)}', {
Thanks for your help
You should definitely use a GET request if your purpose is to fetch the data.
One way of passing the array through the URL is by using a map function to create a comma separated string with all the filters. This way you would not need to know in advance how many elements are in the array. The server can then fetch the string from the URL and split it on the commas.
One more method you can try is to save a filters array on the server side for the session. You can then use a POST/PUT request to modify that array with new filter as user adds or remove them. Finally you can use an empty GET request to fetch the news as the server will already have the filters for that session.
But the fact is, I read everywhere, and I also was teached, that POST request are used when creating or removing, and theoretically, what I should use is a GET request.
Yes, you do read that everywhere. It's wrong (or at best incomplete).
POST serves many useful purposes in HTTP, including the general purpose of “this action isn’t worth standardizing.” (Fielding, 2009)
It may help to remember that on the HTML web, POST was the only supported method for requesting changes to resources, and the web was catastrophically successful.
For requests that are effectively read only, we should prefer to use GET, because general purpose HTTP components can leverage the fact that GET is safe (for example, we can automatically retry a safe request if the response is lost on an unreliable network).
I'm not sure if I could write exactly the same way as my POST request above, but with GET
Not quite exactly the same way
A client SHOULD NOT generate content in a GET request unless it is made directly to an origin server that has previously indicated, in or out of band, that such a request has a purpose and will be adequately supported. An origin server SHOULD NOT rely on private agreements to receive content, since participants in HTTP communication are often unaware of intermediaries along the request chain. -- RFC 9110
The right idea is to think about this in the framing of HTML forms; in HTML, the same collection of input controls can be used with both GET and POST. The difference is what the browser does with the information.
Very roughly, a GET form is used when you want to put the key value pairs described by the submitted form into the query part of the request target. So something roughly like
await fetch('187.345.32.33:3000/fetch-articles?filters=${JSON.stringify(selectedFilters)}', {
method: 'GET'
});
Although we would normally want to be using a URI Template to generate the request URI, rather than worrying about escaping everything correctly "by hand".
However, there's no rule that says general purpose HTTP components need to support infinitely long URI (for instance, Internet Explorer used to have a limit just over 2000 characters).
To work around these limits, you might choose to support POST - it's a tradeoff, you lose the benefits of safe semantics and general purpose cache invalidation, you gain that it works in extreme cases.
In my use case I have Carts and LineItems. My REST service has the following resource urls:
get|post|delete|put api/v1/carts/:cartId
get|post|delete|put api/v1/carts/:cartId/lineItems/:lineItemId
get|post|delete|put api/v1/lineItems/:lineItemId
the probelm is that when I do:
delete api/v1/carts/Default/lineItem/:lineItemId
and then I do:
get api/v1/carts/Default
angular does not hit the server to get the cart, but it gets it from cache which returns the deleted line item back in the cart
I tried all sorts of trick, tried disabling the cache by adding the {cache: false} option, but could not get it to work
the only way I was able to get it to work was if I make a POST request to
api/v1/carts/Default
with an empty body, which tells angular that the resource has changed and clears the cache.
Even though it works it seams like a hacky solution, so I was wandering if someone might have a suggestion of what I am doing wrong.
Get requests are cached by browsers. Simplest way is to use timestamp:
instead of GET /user/100 use GET /user/100?time=120229393
With such request you will always hit server.
If you need this in multiple requests, you can make http interceptor to add timestamp param to request.
I'm trying to find a generic solution to commit/rollback a model alongside ng-resource.
When to commit/save model:
Successful http write methods (PUT, POST, PATCH).
When to rollback/reset model:
Failing http write methods.
User deciding to cancel their local changes (before PUT).
I've found pieces of this solution scattered, but no comprehensive strategy.
My initial strategy was to make use of the default ng-resource cache. Controller starts with GETting an item. If a subsequent PUT/POST/PATCH failed (caught either in the controller's promise, or in the $resource.<write_method>'s interceptor.responseError, re-run the GET that first got the object, and replace my model with that, as on initial load. However, this doesn't help if there's a successful PUT, followed by a failed one, because I'd be replacing my model with a stale object. The same problem occurs when a user tries to cancel their form changes before submitting... the last successful GET may be stale.
I've got a model with ~10 properties, and multiple <form>s (for html/design's sake) that all interact with this one object that gets PUT upon any of the forms being submitted:
{'location_id': 1234,
'address': {
'line1': '123 Fake St'}
...
},
'phone': '707-123-4567',
...
}
Models already have nice rollback features, but getting ng-resource to touch them seems tricky, and I really want to reset the entire object/model. Using a custom cache alongside my $resource instance seems like a decent way to go, but is equally tricky to give user-interaction a way to rollback. Another idea was to store a separate object when loading into scope: $scope.location = respData; $scope._location = angular.copy(respData); that I could load from when wanting to roll back, by way of $scope.location = $scope._location;, but I really don't want to clutter my controllers site-wide with these shadow objects/functions (DRY).
My API calls are automatically being cached in the default cache provided by $cacheFactory ({cache: true}). If I add another record to the DB I want to be able to add the relevant information (which is returned from the post API call to add the record) to the cached information I have rather than deleting the cache and having it re-request all the data.
var $httpDefaultCache = $cacheFactory.get('$http');
var data = $httpDefaultCache.get(key)
The key is the path of my API request. Data is just an array. Element 1 is my stored data which is a hash containing arrays. I planned to simply add to it my new record. However when I retrieve it ($httpDefaultCache.get(key)[1]), I don't get a hash. I get a string. I could just take said string and transform it into a hash or simply add to the string, but I think I'm missing a key component of retrieving data from the cache. Thoughts?
An answer of how I went forward in case anyone runs into a similar problem.
Let's say you used $http to make a get request to an API with cache set to true.
Set the return value of this request within your controller to whatever; we'll use $scope.politicians.
Since we used the default cache, we can retrieve it by $cacheFactory.get('$http')(cachedUrl).
Remove the cache by doing $cacheFactory.get('$http').remove(urlOfGetRequest)
Make any changes I want to the $scope value that was initially set to the return value from the API call and then reset the cache to that scope when finished.
$scope.politicians[new-key] = new-value;
Set the cache again, but use your scope as the value.
$cacheFactory.get('$http').put(urlOfGetRequest, $scope.politicians);
Using the non-default cache makes this process rather easier.
I swear there must be a more direct way of doing this. But I could not find it.
I'm new to AngularJS and I am currently building a webapp using a Django/Tastypie API. This webapp works with posts and an API call (GET) looks like :
{
title: "Bootstrap: wider input field - Stack Overflow",
link: "http://stackoverflow.com/questions/978...",
author: "/v1/users/1/",
resource_uri: "/v1/posts/18/",
}
To request these objects, I created an angular's service which embed resources declared like the following one :
Post: $resource(ConfigurationService.API_URL_RESOURCE + '/v1/posts/:id/')
Everything works like a charm but I would like to solve two problems :
How to properly replace the author field by its value ? In other word, how the request as automatically as possible every reference field ?
How to cache this value to avoid several calls on the same endpoint ?
Again, I'm new to angularJS and I might missed something in my comprehension of the $resource object.
Thanks,
Maxime.
With regard to question one, I know of no trivial, out-of-the-box solution. I suppose you could use custom response transformers to launch subsidiary $resource requests, attaching promises from those requests as properties of the response object (in place of the URLs). Recent versions of the $resource factory allow you to specify such a transformer for $resource instances. You would delegate to the global default response transformer ($httpProvider.defaults.transformResponse) to get your actual JSON, then substitute properties and launch subsidiary requests from there. And remember, when delegating this way, to pass along the first TWO, not ONE, parameters your own response transformer receives when it is called, even though the documentation mentions only one (I learned this the hard way).
There's no way to know when every last promise has been fulfilled, but I presume you won't have any code that will depend on this knowledge (as it is common for your UI to just be bound to bits and pieces of the model object, itself a promise, returned by the original HTTP request).
As for question two, I'm not sure whether you're referring to your main object (in which case $scope should suffice as a means of retaining a reference) or these subsidiary objects that you propose to download as a means of assembling an aggregate on the client side. Presuming the latter, I guess you could do something like maintaining a hash relating URLs to objects in your $scope, say, and have the success functions on your subsidiary $resource requests update this dictionary. Then you could make the response transformer I described above check the hash first to see if it's already got the resource instance desired, getting the $resource from the back end only when such a local copy is absent.
This is all a bunch of work (and round trips) to assemble resources on the client side when it might be much easier just to assemble your aggregate in your application layer and serve it up pre-cooked. REST sets no expectations for data normalization.