While using AngularJS's $http service I've found hard to manage all urls to backend - they're spread around controller's code. Sometimes they're even duplicated. Mostly all of them are in form of $http.get('/api/something').then(...).
To put all of them into different services sounds quite unreasonable: it is just one line of code with may be just small modifications (like adding header etc).
Other solution could be putting them into constants, but in this case I would still use $http.get(APIURLs.SomeURL) that looks like little bit verbose...
So the question is: what is the best way to manage URLs to back-end?
Currently I came up with idea of grouping them into servicies and expose them as calls to $http. Here is my solution:
http://plnkr.co/edit/VjqXHBV54bmjKCuGJ4qX?p=preview
Service:
.factory('AdminAPI', function($http) {
var apiMap = {
'DeleteAuth': {url:'/api/oauth/delete',method:'PUT'},
'GetInvalidUser': {url:'/api/users/invalid',method:'GET'},
'CreateItem': {url:'/api/items/create',method:'POST'}
};
var api = {};
var prepareCall = function(config) {
return function(params) {
var requestConfig = angular.copy(config);
if (config.method=='GET')
requestConfig.params = params;
else
requestConfig.data = params;
return $http(requestConfig);
};
};
for(var name in apiMap)
api[name] = prepareCall(apiMap[name]);
return api;
});
And in controllers I do something like:
AdminAPI.DeleteAuth({data:123}).then(function(result) {
//
});
In this case I have some abstraction (do not have to inject $http into controller), so unit-tests become little bit easier (I do not have to use $httpBackend service, just mock my service calls).
Make use of angular Module.constant:
Move reusable settings to single object;
Define angular module wrapping this object:
angular.module('myApp.constants', []).constant('config', {
ROUTES: {
home: '/home.html',
about: '/about.html'
}
});
load myApp.constants module into application with all the others:
angular.module('myApp', [
'myApp.constants',
'myApp.services',
'myApp.controllers',
'myApp.filters',
'myApp.directives'
]);
After that you can inject config dependency and access defined hash.
Good question. My suggested approach is to put the constants in a constantdeclaration like this:
angular.module('fooApp').constant('config', {
api: {
baseUrl: 'http://api.example.com/api',
key: 'SECRET_API_KEY',
}
});
And you could implement a httpResourceFactory that is responsible for creating $http or $resource references that points to different endpoints using the constants defined in config.
This could be implemented in a service.
Then all API endpoint definitions can be put into a single service.
angular.module('fooApp').factory('dataService', ['httpResourceFactory', function(httpResourceFactory) {
var PUBLIC_API = {};
PUBLIC_API.Orders = httpResourceFactory('/orders/:id');
PUBLIC_API.Users = httpResourceFactory('/some-url/users/:id');
return PUBLIC_API;
}]);
Then in your controller you may inject dataService and do stuff like this:
$scope.users = dataService.Users.query();
Hope this clarified it. I'm in a hurry, so ask more concrete and I will provide more examples later on.
Related
Angular JS conceptual overview. View-independent business logic: Services (heading).
The description says - moving view-independent logic from the controller into a service, yet the code says factory. What am I missing here?
angular.module('finance2', [])
.factory('currencyConverter', function() {
var currencies = ['USD', 'EUR', 'CNY'];
Link to the resource
The factory method ('recipe') is a way of creating a 'Service'.
You can also create 'Services' with the service, constant, value, and provider recipes ('methods').
However you do it, you'll end up instantiating an object that is conceptually a 'Service'.
It's been acknowledged widely that this is a confusing aspect of Angular. See this classic Stackoverlow question.
The developer guide does a good job of clarifying these concepts too:
Each web application you build is composed of objects that collaborate to get stuff done. These objects need to be instantiated and wired together for the app to work. In Angular apps most of these objects are instantiated and wired together automatically by the injector service.
The injector creates two types of objects, services and specialized objects.
Services are objects whose API is defined by the developer writing the service.
Specialized objects conform to a specific Angular framework API. These objects are one of controllers, directives, filters or animations.
The injector needs to know how to create these objects. You tell it by registering a "recipe" for creating your object with the injector. There are five recipe types.
The most verbose, but also the most comprehensive one is a Provider recipe. The remaining four recipe types — Value, Factory, Service and Constant — are just syntactic sugar on top of a provider recipe.
Coming from a Java background, I really like the concept of angular factories; we get to mimic POJOs here to an extent. I get to attach meaningful methods and execute logic that's all self-contained within the model itself. Whereas for services, I tend to treat those as I'd treat a service on the server-side, simply for fetching data.
For instance, if we were building a Twitter clone of some sort, for the tweet stream, I'd have a TweetSteamFactory that internally fetches data using TweetService to get the latest tweets. Maybe my factory has a getNextPage() method, which is bound to an ngClick somewhere - when fired, it of course makes its call with TweetService.
At any rate, I do see a pretty clear distinction between services and factories, although my understanding could be misguided.
http://plnkr.co/edit/SqPf212nE5GrSPcZdo5K
Controller
app.controller('MyController', function(FoobarFactory) {
FoobarFactory()
done(function(factory) {
var factory = factory;
$scope.data = factory.getData();
$scope.baz = factory.getBaz();
})
});
Factory
app.factory('FoobarFactory', ['MyService', function(MyService) {
function Foobar() {}; // Empty constructor
angular.extend(Foobar.prototype, {
_init: function() {
var deferred = $.Deferred();
var foobar = this;
this.baz = true;
this.data = undefined;
MyService.getData()
.done(function(data) {
foobar.data = data;
deferred.resolve(foobar);
})
deferred.resolve();
return deferred.promise();
},
getBaz: function() {
return this.baz;
},
getData: function() {
return this.data;
}
});
return function () {
var deferred = $.Deferred();
var foobar = new Foobar();
foobar._init()
.done(function() {
deferred.resolve(foobar);
})
.fail(function(error) {
deferred.reject(error);
});
return deferred.promise();
};
}]);
I'm building a small two-language app with the use of angular-translate. I want to have a language switcher in every view (controller). I'm trying to figure out how to put the code responsible for language switching into every controller. The code looks like this:
var langSwitch = $Scope.setLang = function (langKey) {
$translate.use(langKey);
};
So far I've figured that I can create a factory that looks like this:
app.factory('langSwitch', function ($rootScope, $translate) {
var langSwitch = $rootScope.setLang = function (langKey) {
$translate.use(langKey);
};
return langSwitch;
});
and inject it into controllers in this maner:
app.controller('HomeCtrl', function (langSwitch) {
// normal controller code here
});
This works but 1) I'm using $rootScope and I have a feeling this is bad practice & 2) jsHint screams that "langSwitch" is not defined. Maybe there is a simpler way to make the function global without putting it into every controller?
I'm still pretty new to Angular so don't scream at me :) Thanks.
edit
My view:
<button ng-click="setLang('en_GB')">English</button>
<button ng-click="setLang('pl_PL')">Polish</button>
Although you got the idea, you overcomplicated things a bit. You could declare the service as follows:
app.service('langSwitch', function ($translate) {
this.setLang = function (langKey) {
$translate.use(langKey);
};
});
And then inject langSwitch in the controller responsible for lang switching, as you already did. No need to inject $rootScope in the service.
You don't need $rootScope indeed unless you need to process some global events in your application. All services and factories in angular are singletons by default. That means once it created, it will be passed as the same instance in every place it is declared as a dependency. So if you want to share data and functionality between different controllers - the services will suit fine. You can change your factory code to:
app.factory('langSwitch', function($translate) {
return {
setLang: function(langKey) {
$trasnlate.use(langKey);
};
};
});
I'd like to know how what is the best (and most secure) way to pass parameters (such as product id) between views so the user cannot see them in the url bar.
Thank you.
Stephan
You should pass them from one controller to another controller in the new view. Here is a pretty good answer on how to do that: LINK
Store them in a shared service and use them when you need them, quick sample:
app.factory("shared", function() {
var data = null;
return {
setData: function(someData) {
data = someData;
},
getData: function() {
return data;
}
}
});
And now use it!
app.controller("myCtrl", function($scope, shared) {
$scope.data = shared.getData();
});
A good way to do this is to use the Angular Service pattern.
Because the controllers are functions and the services are objects (singletons) You can have multiples services according to the differents functionnalities that you have in your app.
You should see the documentation for Angular providers here :
https://docs.angularjs.org/guide/providers
I'm still learning AngularJS, and have a question regarding their flavor of dependency injection. For example purposes, say I have a DataProcessor service which has a processData method that takes in a uri parameter and it needs to read that data (which may be xml, json, etc.) and then perform some actions on it. The DataProcessor constructor takes in an implementation of a DataReader interface that knows how to read a certain file type. Here are some example services of what I'm talking about:
// implementations of the DataReader interface
myApp.service('XmlDataReader', function() {
this.readData = function(uri) {
// read xml data from uri
}
}]);
myApp.service('JsonDataReader', function() {
this.readData = function(uri) {
// read json data from uri
}
}]);
// data processing service that takes in an implementation of a DataReader
myApp.service('DataProcessor', ['DataReader', function(DataReader) {
this.processData = function(uri) {
var readData = DataReader.readData(uri);
// process data and return it
}
}]);
From a typical dependency injection perspective, a specific type of DataReader could be passed into the DataProcessor and used like so:
var dataProcessor = new DataProcessor(new JsonDataReader());
var processedData = dataProcessor.processData('dataz.json');
What is the AngularJS way of doing this?
Do something like this:
myApp.service('DataProcessor', ['$injector', 'valueRecipeOfTheServicename', function($injector, valueRecipeOfTheServicename) {
this.processData = function(uri) {
var service = $injector.get(valueRecipeOfTheServicename);
// process data and return it
}
}]);
$injetcor.get() retrieves a service
Based on Noypi Gilas answer, I am initiating the controller with the name of the service and retrieving it via $injetcor.get():
myApp.service('DataProcessor', ['$injector', function($injector) {
var service;
$scope.init = function (serviceName) {
service = $injector.get(serviceName);
}
this.processData = function(uri) {
// use the service ...
}
}]);
Because of the way DI works - you shouldn't have to create instances of your services, ever really. What you do is you inject the service(s) you need into your controller and it should just work. In the case above, your controller might be defined to be:
var app = angular.module('App', ['DataProcessor']);
function MyController($scope, DataProcessor) {
var uri = '';
DataProcessor.processData(uri);
}
The only other thing you need to do here is make sure that "App" is the name you specify in the "ng-app" directive and make sure that your page includes the JS files with "DataProcessor" before you include the angular app module (technically these could even be defined in the same file). Hope this helps!
Edit
By the way, if you need to minify - the following is how you would define the controller:
var app = angular.module('App', ['DataProcessor']);
// if you need to minify:
var MyController = ['$scope', 'DataProcessor',
function($scope, DataProcessor) {
var uri = '';
DataProcessor.processData(uri);
}
];
Additional Suggestions
My understanding of a service at the present time is that is is used to shared data or code between controllers. If this data processing is specific to that controller you might consider just moving the "ProcessData" implementation directly into your controller. Sometimes changes like this can be simpler than processing the data in a service. If you do process the data in the service you might still want to write that data back to the scope. In this case, you can pass $scope as a parameter into the service routine. Since I don't know too much about your use case, just take these suggestions with a grain of salt. Good luck!
I am working on an angularJS app and I am trying to stick with the most efficient and widely accepted styles of development in AngularJs.
Currently, I am using this way of declaring my services like so:
app.factory('MyService', function() {
/* ... */
function doSomething(){
console.log('I just did something');
}
function iAmNotVisible(){
console.log('I am not accessible from the outside');
}
/* ... */
return{
doSomething: doSomething
};
});
However, there are numerous examples out there and I am not quite sure which design style to follow. Can someone with extensive knowledge about services explain the reason why a certain style is more relevant than another?
Is what I am doing useful in any way other than restricting the access to certain functions in my service?
I would suggest you layout your factory or service as they do in the angular-seed app, except that app annoyingly only uses value in the services.js boilerplate. However you can adapt the layout they use for controllers, directives and filters.
'use strict';
/* Filters */
angular.module('myApp.filters', []).
filter('interpolate', ['version', function(version) {
return function(text) {
return String(text).replace(/\%VERSION\%/mg, version);
}
}]);
Which means for a service you would do:
'use strict';
/* Services */
angular.module('myApp.filters', []).
service('myservice', ['provider1', 'provider2', function(provider1, provider2) {
this.foo = function() {
return 'foo';
};
}]).
factory('myfactoryprovider', ['myservice', function(myservice) {
return "whatever";
}]);
This has more boilerplate than you absolutely need, but it is minification safe and keeps your namespaces clean.
Than all you have to do is decide which of const, value, factory or service is most appropriate. Use service if you want to create a single object: it gets called as a constructor so you just assign any methods to this and everything will share the same object. Use factory if you want full control over whether or not an object is created though it is still only called once. Use value to return a simple value, use const for values you can use within config.
I don't believe there's an accepted standard but I myself follow this convention:
var myServiceFn = function (rootScope, http) { ... };
...
app.factory('MyService', ['$rootScope', '$http', myServiceFn]);
I feel like this is cleaner than defining the function inline and also allows for proper injection if I ever decide to minify my files. (see http://docs.angularjs.org/tutorial/step_05).
As an example, I've been defining services within modules like so:
angular.module('userModule', [])
.service('userService', function() {
this.user = null;
this.getUser = function() {
return this.user;
};
this.userIsNull = function() {
return (this.user === null);
};
this.signout = function() {
this.user = null;
};
})
I think it is more clear to use the .service rather than the .factory?