Factory patterns and ngResource - angularjs

I have a factory that provides access to a resource object as shown below. Initially, I had this implemented inside a controller, but moved it up into a factory to make it more reusable across my whole app.
When I was building it, a line in the angular doc on providers caught my eye:
Best Practice: name the factory functions as Factory (e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase or looking at stack traces in the debugger. (source)
I'm obviously not doing that below (as I'm following a pattern from the resource Doc). But then I was trying to figure out what kind of provider this would qualify as and I'm not sure, and they don't include Best practice notes for any of the other kinds of providers detailed. Should I use service() instead of factory()? (I can't really find a clear answer.)
This question obviously has very low stakes, as the application works just fine (better than it did when I wasn't using the factory). But I'm curious about opinions.
...
.factory("Entries",Entries)
...
function Entries($resource,config) {
endpoint = config.endpoint + ":" + config.port + "/entry";
return $resource(endpoint + '/:id', {id:'#id'},
{ 'get': {method:'GET'},
'create': {method:'POST'},
'update': {method:'PUT'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
}
);
}

If it works as a factory, keep it as a factory. I never use the service provider as I find that it doesn't provider anything over the factory method, and it is easier for my brain to grasp the factory pattern.
With a factory, you provide a function that returns the object you want angular's injector to use. With a service, you provide a Constructor function. The injector then creates the object that will be injected by newing up an object using the function provided. Angular factory's and service's are always singletons though so providing a Constructor function to new up a single object seems to me like a bit of overkill.
In your situation, if you converted it to a service, your object would basically just be acting as a proxy, something like below
...
.service("Entries",Entries)
...
function Entries($resource,config) {
var endpoint = config.endpoint + ":" + config.port + "/entry";
var resource = $resource(endpoint + '/:id', {id:'#id'},
{ 'get': {method:'GET'},
'create': {method:'POST'},
'update': {method:'PUT'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
}
);
this.get = resource.get;
this.create = resource.create;
this.update = resource.update;
this.query = resource.query;
this.remove = resource.remove;
this.delete = resource.delete;
}
That's pretty ugly and serves no purpose unless you really want to abstract away that you are using a $resource, which again you could do with a factory just as easily
...
.factory("Entries",Entries)
...
function Entries($resource,config) {
var endpoint = config.endpoint + ":" + config.port + "/entry";
var resource = $resource(endpoint + '/:id', {id:'#id'},
{ 'get': {method:'GET'},
'create': {method:'POST'},
'update': {method:'PUT'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
}
);
return {
get: resource.get,
create: resource.create,
update: resource.update,
query: resource.query,
remove: resource.remove,
delete: resource.delete
}
}
Even the angular documentation can't seem to provide a compelling reason for using a service. The example they give for when a service is most suitable is this (taken from here)
Given that you have an existing Constructor function
function UnicornLauncher(apiToken) {
this.launchedCount = 0;
this.launch = function() {
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
}
a factory provider would look like
myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
return new UnicornLauncher(apiToken);
}]);
while with a service you could make it a oneliner
myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Not too exciting to me and certainly not a good reason to make me go through the thought exercise of whether this should be a service or a factory. My advice is to forget that service() exists and just use factories

Related

How do I call $resource service from controller in angularJs

This is get service function where I am calling the API
.factory('Report', function($resource, API_URL) {
return $resource(API_URL +
'security/:userId/1498780800000/listOfDeliveries', {
userId : '#userId',
expected : '#expected',
arg1 : '#arg1'
}, {
update: {
method: 'PUT'
}
});
})
In the app.js I have this below controller
.controller('ReportsController', function($scope, $rootScope,
ProfileData, $state, $timeout, Report) {
})
First of all, you need to check how angular factory & service work.
Your factory return a $resource, so read the doc about $resource
A resource "class" object with methods for the default set of resource
actions optionally extended with custom actions. The default set
contains these actions:
{
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
So, you can use theses methods: Report.get(), Report.save(), Report.query(), Report.remove(), Report.delete()
In addition there are custom method you defined: Report.update()
And you can pass userId as params so:
Report.get({userId: 1234}); will call a GET request to: API_URL+'security/1234/1498780800000/listOfDeliveries
(expected and args1 are not in url so I dont think you need them)
What's returning Report.get() ?
Class actions return empty instance (with additional properties below). Instance actions return promise of the action
So Report.get(...) is returning a promise, you will get data by:
Report.get(...).then(function(data) {
$scope.requestData = data;
});

Store domain in one place in angular js service

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

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';
}]);

Angular - correct approach to handle the data fetch from server?

Assume that, I am using the angular-resource - module with my ng-app. I don't able to understand the data handling easy and scale able way.. any one give me / show me a correct way to answering all this questions?
a) Generally how to fetch data from url in the angular.?
b) in case each of the controller may require different url and data if so how the fetch process added on each of controller.?
c) or need we make a service to provide the data according to the controllers parameters - if so how to pass parametes to service?
d) All above have GET, PUT and DELETE, 'POST` then how to handle all them - is all this need separate services?
Thanks in advance.
Use angular-resource as you said within a service/factory. It already provides a lot of your requirements:
myApp.factory("dataService", [
"$resource",
function ($resource) {
return $resource("http://someBaseUrl/:action/:id", {
id: "#id" // default parameters
},
{
// custom methods
update: { method: "PUT" },
doOtherStuff: { method: "GET", action: "DoOtherStuff" }
});
}
]);
The $resource default provides for the following REST compliant functions:
{
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
Any other functions you have to include yourself, like the update and the doOtherStuff in the example above.
The :action part is an easy way to provide any custom action, that is not REST compliant.
Usage in controllers:
myApp.controller("myCtrl", [
"dataService",
function (dataService) {
// first parameter can be used for any extra query parameters
// second parameter always is a callback
var myData = dataService.query({}, function() {
// success
});
var mySingleInstance = dataService.get({ id: 12 });
this.doUpdate = function (entity) {
dataService.update(entity);
// Or, if the 'entity' is a resource entity:
// entity.$update();
}
this.customMethod = function () {
dataService.doOtherStuff();
}
}
]);
See https://docs.angularjs.org/api/ngResource/service/$resource for the full documentation

Error: [$resource:badcfg] Error in resource configuration. Expected response to contain an array but got an object?

How fix Error:
[$resource:badcfg] Error in resource configuration. Expected response
to contain an array but got an object?
// Service
angular.module('admin.services', ['ngResource'])
// GET TASK LIST ACTIVITY
.factory('getTaskService', function($resource) {
return $resource('../rest/api.php?method=getTask&q=*',{ 'get': {method:'GET'}});
})
// Controller
$scope.getTask = getTaskService.query(function (response) {
angular.forEach(response, function (item) {
if (item.numFound > 0) {
for(var i = 0; i < item.numFound; i++) {
$scope.getTasks[i] = item.docs[i];
}
}
});
});
Also, if your service is sending an object instead of an array add isArray:false to its declaration.
'query': {method: 'GET', isArray: false }
$resource("../rest/api"}).get();
returns an object.
$resource("../rest/api").query();
returns an array.
You must use :
return $resource('../rest/api.php?method=getTask&q=*').query();
First of all you should configure $resource in different manner: without query params in the URL. Default query parameters may be passed as properties of the second parameter in resource(url, paramDefaults, actions). It is also to be mentioned that you configure get method of resource and using query instead.
Service
angular.module('admin.services', ['ngResource'])
// GET TASK LIST ACTIVITY
.factory('getTaskService', function($resource) {
return $resource(
'../rest/api.php',
{ method: 'getTask', q: '*' }, // Query parameters
{'query': { method: 'GET' }}
);
})
Documentation
http://docs.angularjs.org/api/ngResource.$resource
In order to handle arrays with the $resource service, it's suggested that you use the query method. As you can see below, the query method is built to handle arrays.
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
User $resource("apiUrl").query();
Make sure you are sending the proper parameters too. This happened to me after switching to UI-Router.
To fix it, I changed $routeParams to use $stateParams in my controller. The main issue was that $stateParams was no longer sending a proper parameter to the resource.
For anyone coming here in 2021, another way this request may look is as follows (at least it was in our app)
angular.module('load').factory('StateService', ['$resource', 'store', function($resource, store) {
return $resource('/some/url', {}, {
fetchAllStateCodes: {
method: 'GET',
isArray: true, // Response is an array of objects
headers: {
"Authorization": "Bearer " + store.get("jwt"),
"Content-type": "application/json"
}
}
});
}]);
From all of the good answers, it still wasn't obvious to me as to where to put the isArray flag...

Resources