Passing a path with '/' to $resource or $http factory - angularjs

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.

Related

ngResource custom model actions

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.

Angularjs $resource Cache

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

AngularJS $resource Headers POST

I would like to know how to pass headers to AngularJS $resource method
Here is the factory method
.factory('DataRepository', function ($resource) {
return $resource(serviceUrlPrefix + '/api/v1/AppList/:id', { id: '#id' }, { 'query': { method: 'GET', isArray: false }, 'update': { method: 'PUT', AppList: '#req', headers: { 'X-Requested-With': 'XmlHttpRequest' } } });
});
Here is the call to the dataRepository
dataRepository.update({ id: req[uniqueIDColumn] }, req, function (data) {
},
function (error) {
});
This code works fine. But i have few queries
Question 1:
Rather than specifying the headers in the factory method , how can i specify it in the call to the factory method? I tried few methods but it didnt work out.
Question 2:
I specified the header in the update method in the factory. When i perform "Save" using that factory, that header has been taken by default. But i have specified it explicitly for PUT method. Right? Why and how?
Question 3:
If i would like to specify the header for the particular factory in common for all Http methods, what is the way to do it?
Question 4:
What is the nomenclature for passing the parameters and the significance of "#" symbol before parameter and also in the below part, AppList is the parameter name used in the WebAPI, is it mandatory that it should match the parameter name in the WebAPI method, if its not matching, its not working:(
AppList: '#req'
I'm afraid we don't use $resource, but it does depend on $http. We configure the header with the below. Not sure about the rest of your questions.
I will say that we also do not use $http directly. We created our own "requestHelper" service that we inject everywhere. That allows us to inject things before making calls to $http as well as catch the response before passing the result on to the real caller. Helps with common error handling.
Configure headers for $http:
module.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}]);

AngularJS: Resource factory and callback function

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

Can a call to $http be made more concise?

I find myself frequently using this construct in my controllers:
$http({method: 'GET', url: '../ajax/mycallback', cache: false}).
success(function(data) {
$scope.data = data;
});
Is there any way to make this more concise? In particular, is there a kind of "default success method" that just stores the result on the scope? Also, is there a way to globally set cache to false, so I can use $http.get() ?
Option 1: you only need to pass the value to $scope
$http returns a promise, so you can share the returning result directly with the scope:
$scope.data = $http({method: 'GET', url: '../ajax/mycallback', cache: false})
Angular has automatic promise fulfillment support in templates. So if you assign the data to templates, it will automatically trigger the digest cycle upon fulfillment (read: it will trigger the update when the $http will succeed).
But in this case I would suggest you to use $resource instead of $http and incapsulate the $resource creation as a service.
You also can use $resource the same way, as it has natural automatic promise fulfillment. Natural states that promises will be replaced with actual values (this logic includes arrays support!):
$scope.data = MyResourceService(...)
Option 2: you have a more complex callback
You can simply create a factory function to create a closure within a specific scope:
function cb(scope, scopeObjName) {
return function(data){
scope["scopeObjName"] = data;
// common logic goes here
}
}
and use it like this:
$http({method: 'GET', url: '../ajax/mycallback', cache: false})
.success(cb($scope, "data"))
As you can see, the answer depends on the actual usecase and can be improved further.
You could do the following:
myApp.config(function($httpProvider){
$httpProvider.defaults.cache = false;
});
as for your callback, If that is all you're doing, the anonymous function is the best option. Otherwise you'd have to bind a generic function every http invocation and pass it a context..like this:
$http.get(...).success(standardSuccess.bind({$scope:$scope,p:'data'}));
function standardSuccess(data){
this.$scope[this.p] = data;
}
Then you would just keep that in an injectable somewhere. But as you can see is more verbose and less readable. And slower.
Another option is to pass the scope into the httpConfig itself. and use an interceptor to set your property. (as the config is received in an interceptor. Roughly like:
myApp.config(function($httpProvider){
$httpProvider.defaults.cache = false;
$httpProvider.interceptors.push('defaultHttpSetter');
});
myApp.factory('defaultHttpSetter',function($q){
return {
'response': function(response){
response.config.scope[response.config.prop] = response.data;
return $q.when(response);
}
};
});
Which would be used like..
$http.get('/myurl',{scope:$scope,prop:'data'});
I don't have issue with the other answers, but just in case they are over-thinking it. You can do this:
var genericSuccessFunction = function(data) {
$scope.data = data;
}
You can create your result function as part of the controller and save it as a controller local variable.
Then you just need to pass the result function into the success:
$http({method: 'GET', url: '../ajax/mycallback', cache: false}).success(genericSuccessFunction);
In my experience, I often find myself using this approach for an error function, but I don't often find myself able to use generic success functions.

Resources