I have a more generic implementation of $resource as "Datarepository" factory in my application and all the REST API calls calls this factory to do the "REST" operation
myapp.factory('DataRepository', function ($resource) {
var resourceFactory = function (url) {
return $resource(url, {}, {
update: { method: 'PUT' }
}
);
}
return {
invokeAPI: resourceFactory
}
});
A sample call to the repository get method looks like this
DataRepository.invokeAPI(myappURL).get();
For a specific scenario alone, i would like to "cache" the data.. I dont want to disturb the "Datarepository" factory method and just would like to add the cache paramter for those URL's which i would like to cache the data
something like this
DataRepository.invokeAPI(myappURL).get({cache:true});
The above implementation doesnt work the way it is expected and it passed the cache as query string paramter. I read the angularjs documentation for $resource. I got it how to set it at $resource level but i m not sure how to pass it to the resource through normal function call without disturbing the Factory implementation
Related
I have the following example method in angular service:
function send(data) {
return $http({
method: 'POST',
url: 'https://test.domain/test/send',
data: $httpParamSerializerJQLike(data)
});
}
The domain that is https://test.domain/test is the same for all the services in my app. I do not want to write it every time in every services. I can abstract it in a constant and inject it in every service but I wonder if there is more clever solution. Is it possible to store the domain part in an interceptor or any other suggestions are welcome. Please provide code examples as I am quite new to angular. Thanks
I'd say rather than abstracting the values out into a constant, you should abstract the $http call into a service. Then you can just inject that service into all of your other services in place of $http. For example:
angular.module('myApp').factory("myHttp", ["$http", function ($http) {
return function (config) {
config.url = "https://test.domain/test" + config.url;
return $http(config);
};
}]);
So effectively what this service is doing is proxying calls to $http, but prepending your common URL to the beginning - this would allow you to change your example code to:
function send(data) {
return myHttp({
method: 'POST',
url: '/send',
data: $httpParamSerializerJQLike(data)
});
}
Of course, this is just one example of how you could do an abstraction like this - the myHttp service could take any form you like, depending on what would be most convenient for you. I think this is a better solution than using an interceptor in this case, as it allows you to pick and choose when you use it, rather than it being applied to every single HTTP request.
create an interceptor and on requests change the url.
angular.module('app').factory('domainInterceptorService', [
function () {
var request = function (config) {
config.url = 'https://test.domain/' + config.url;
}
return config;
}
return {request: request};
});
I'm using ngResource to handle my models in my Ionic/Angular app and I'm having trouble figuring out how/if I can make custom actions on the resource.
I'm storing my model instances in local storage and when I update a record, I want to update the local storage as well. I have this working, but I'm having to copy and paste code for multiple instances and would like to keep it DRY.
LogEntry.update($scope.timeLog, function(data) {
// update local storage
for ( var i = 0; i < logEntries.length; i++) {
if(logEntries[i].id == $scope.timeLog.id){
logEntries[i] = $scope.timeLog;
}
};
localStorageService.set('LogEntries', logEntries);
});
Here is a situation where I update a record, and after the promise returns I update local storage. I would like to make this repeatable, how I envision it being possible (based on other things I've seen in other frameworks and other languages) is something like:
LogEntry.update($scope.timeLog, function(data) {
// update local storage
LogEntry.updateLocalStorage($scope.timeLog);
});
My resource looks like:
.factory('LogEntry', function(config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/', {}, {
'update': {
method:'PUT',
params: { id: '#id' }
}
});
})
Maybe I'm missing something in the docs, but it's pretty short and I'm not seeing a way to do this. Is something like LogEntry.updateLocalStorage($scope.timeLog); possible to store with ngResource, or do custom actions like that need to come from somewhere else? I'd like to keep my model-related actions together if possible.
You could use the transformResponse method in your resource definition. It's kind of a hack since you don't actually need to alter the response, but it allows you to preform actions with the returned data:
{function(data, headersGetter)|Array.<function(data, headersGetter)>}
transform function or an array of such functions. The transform function takes the http response body and headers and returns its transformed (typically deserialized) version. By default, transformResponse will contain one function that checks if the response looks like a JSON string and deserializes it using angular.fromJson. To prevent this behavior, set transformResponse to an empty array: transformResponse: []
https://docs.angularjs.org/api/ngResource/service/$resource
.factory('LogEntry', function (config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/', {}, {
'update': {
'method': 'PUT',
'params': {
'id': '#id'
},
'transformResponse': function (data, header) {
// do stuff with data
return data;
}
}
});
})
That way it will always execute when you get a response but you could also use the transformRequest method, which will always fire on request regardless if you're getting a response.
{function(data, headersGetter)|Array.<function(data, headersGetter)>}
transform function or an array of such functions. The transform function takes the http request body and headers and returns its transformed (typically serialized) version. By default, transformRequest will contain one function that checks if the request data is an object and serializes to using angular.toJson. To prevent this behavior, set transformRequest to an empty array: transformRequest: []
https://docs.angularjs.org/api/ngResource/service/$resource
Which you choose depends on your usecase. Using transformRequest you could always save to localstorage, even when remote is down.
Hi being new to Angular i'm having difficulty seeing how to handle data when using $resource in a factory. I'm trying to move from $http requests to $resources in my factory.
First i had (simplified code):
.factory('MetadataService', function($http) {
$http({
method: 'OPTIONS',
url: 'http://myurl'
}).success(function(data) {
customized_data = do_some_complex_handling_on_data(data)
callback(customized_data);
});
});
When i try to use a $resource in my factory, it seems that i have to call the do_some_complex_handling_on_data() method in my controller:
.factory('MetadataService', function($resource) {
return($resource('http://myurl', {}, {
metadata: {method: 'OPTIONS'}
}));
});
# controller:
var metadata = do_some_complex_handling_on_data(MetadataService.metadata());
Since i'm gonna use the factory in a lot of controllers for different sections in my application (that's why i made a factory in the first place), i would like to have my factory return the data as i need it. And not have to customize the data after the factory returns it.
question: How do i let my factory call the do_some_complex_handling_on_data() function instead of the controller?
You can use response transformer that $http service provides. A transformer is used to transform the response of $http before it is delivered to the end client.
By default there is a single transformer register that convert json string to json object. You can append your own transformer to this collection and it will be called with the response json object. In your transformer function you can then call any function you want that can transform the data.
metadata: {
method: 'OPTIONS'
transformResponse: appendTransform($http.defaults.transformResponse,
function(value) {
return do_some_complex_handling_on_data(value);
})
}
function appendTransform(defaults, transform) {
// We can't guarantee that the default transformation is an array
defaults = angular.isArray(defaults) ? defaults : [defaults];
// Append the new transformation to the defaults
return defaults.concat(transform);
}
I have taken this code from the docs here
Also read documentation on "Default Transformations" in $http service
I have a REST backend which provides a hashmap of API methods. This allows the front end to use a keyname to map to the API method instead of needing to know the entire REST url.
Ex:
{
CHARGE.GET_CHARGE_INFO: /provider/charge/getChargeInfo/{{providerId}}/{{chargeId}}
CHARGE.LIST_CHARGES: /provider/charge/listCharges/{{providerId}}
CHARGE.LIST_TYPES: /provider/charge/listTypes
}
There is a service which is responsible for loading the hashmap from the REST API.
app.service('preloader', function ($http, globals, $interpolate, $q) {
var self = this;
var endPointsList = $http.get(globals.apiPath + globals.endPointsPath, {}).success(function (data) {
self.endPoints = data;
});
return {
endPointsList: endPointsList,
getResourceEndPoint: function( endPoint ){
if (!self.endPoints[endPoint]) {
// To avoid misleading errors
throw ("Endpoint not found. \r\nEndpoint: " + endPoint + "\r\nFound: " + self.endPoints[endPoint]);
}
// reformat
return globals.apiPath + self.endPoints[endPoint].replace(/{{([^}]+)}}/g, ":$1");
}
};
I'm trying to write a $resource which uses this service, but the problem is when the service is injected into the factory, it has not yet been initialized and consequently calls to the method to retrieve the endpoint throw errors.
app.factory('providerFactory', ['$resource', 'preloader',
function ($resource, preloader) {
return $resource( "", null,
{
"getTypes" : {
method: 'GET',
url: preloader.getResourceEndPoint('PROVIDER.LIST_TYPES')
},
"get": {
method: 'GET',
url: preloader.getResourceEndPoint('PROVIDER.GET_PROVIDER_INFO'),
params: {
providerId: '#providerId'
}
}
}
);
});
Error Msg:
TypeError: Cannot read property 'PROVIDER.LIST_TYPES' of undefined
at Object.getResourceEndPoint (http://localhost:8888/client.git/build/app.min.js:3:8967)
If I step through the code, I see that the factory's preloader.getResourceEndPoint() calls are calling the service before the service has completed its initialization of the endPointsList.
What can I do to avoid this problem? Do I have to use a $q promise? It seems like overkill for something like this. If so then I have to wonder if this is even the right approach to get something like this to work, but I'm not sure what approach to use.
I'm still learning AngularJS so I'm not entirely sure of the correct way to proceed. Ive inherited this code so if it seems that it is convoluted/complicated for nothing, I'd appreciate insights on how to improve it.
Is there a way to pass a parameter containing / to Factory? Want to accomplish something like
.factory('MyData', ['$resource', function ($resource) {
return $resource('http://1.2.3.4/:urlFragment', {
urlFragment : '' // default empty
}, {
getData : {
method : 'GET'
},
And calling it
$scope.scopeVar = MyData.getData({urlFragment : '/some/path/to/data'});
Looking at the network console, I see that / are replaced with %2.
Can I encode the passed parameter inside Factory? (Using $http or $resource).
Or in general, how can I execute any functions on parameters inside factory?
No, you can't really get access to the url inside of your factory because $resource automatically handles it. But thankfully Angular gives you a way to get access to the url before it is called by using the $resource directly. Looking at the docs here, one of the actions you can supply in your $resource declaration is a transformRequest property.
return $resource('http://1.2.3.4/:urlFragment', {urlFragment: ''}, {
getData: {method: 'GET', transformRequest: function(data, headers){
// make your modifications here to either data or headers
}}
});
Although I haven't actually run this code, I believe that should allow you to do what you want. Let me know if it doesn't.