Ran Mizrahi from CodeOasis gave a lecture about Angular.js dependency injections, as part of an advanced Angular meetup of the Javascript Israel meetup group.
Ran covered how Dependency Injection works in Angular and its internals, what are providers and the different helper methods angular have for defining injectables components.
2. About CodeOasis
• CodeOasis specializes in cutting-edge web solutions.
• Large variety of customers (from startups to enterprises).
• We LOVE JavaScript (-:
• Technologies we love:
• Symfony2
• AngularJS
• nodeJS
• HTML5
• CSS3
• Our Microsoft department works with C#, WPF, etc.
Monday, September 9, 13
4. The Problem
function SessionStore() {}
SessionStore.prototype = {
set: function(name, value) {
window.localStorage.setItem(name, value);
},
get: function(name) {
return window.localStorage.getItem(name);
}
}
function User() {
this.sessionStore = new SessionStore();
}
User.prototype = {
setSession: function(session) {
this.sessionStore.set('session', session);
},
getSession: function() {
return this.sessionStore.get('session');
}
}
Monday, September 9, 13
5. The Problem
Everything goes well and the code is working.
But now we have to change the session name...
We can use a global variable:
sessionName = 'newSessionName';
function User() {
this.sessionStore = new SessionStore();
}
// ...
Maybe we should hard-coded pass it to the store:
function User() {
this.sessionStore = new SessionStore('newSessionName');
}
// ...
Monday, September 9, 13
6. The Problem
How would you change the entire SessionStore
implementation?
Cookies
Server
...
function User() {
this.sessionStore = registry.get('SessionStore');
}
// ...
Should we use some global registry object??
How would we test the User??
Monday, September 9, 13
7. The Problem
All those solutions are bad because:
• Couples code to one implementation.
• Hard to configure
• Cannot change the implementation without changing the
User prototype.
• Untestable code unless you monkey patch it.
Monday, September 9, 13
8. Dependency Injection
Dependency injection is a software
design pattern that allows the
removal of hard-coded
dependencies and makes it possible
to change them.
-- Wikipedia
Monday, September 9, 13
9. Dependency Injection To The Rescue!
Instead of instantiating the SessionStore within the User...
function User(sessionStore) {
this.sessionStore = sessionStore;
}
// ...
var user = new User(new SessionStore());
JUST INJECT IT!!!
Monday, September 9, 13
10. Advantages Of Using DI
Use different session store strategies...
function User(sessionStore) {
this.sessionStore = sessionStore;
}
// ...
var user = new User(new CookieSessionStore());
Monday, September 9, 13
11. Advantages Of Using DI
Configuration becomes easy...
function User(sessionStore) {
this.sessionStore = sessionStore;
}
// ...
var user = new User(new SessionStore('mySessionName'));
Monday, September 9, 13
12. Advantages Of Using DI
Now we can easily mock the SessionStore (for testing
purposes)...
function User(sessionStore) {
this.sessionStore = sessionStore;
}
// ...
var user = new User(new MockSessionStore());
Monday, September 9, 13
13. All those things makes our code
MAINTAINABLE
because we’ve..
Less code
Extensible
Testable
Monday, September 9, 13
14. But How To Scale It?!!?!?
But what if we have a slightly more complex application..
And we do that in many different places around our application:
var user = new User(new SessionStore(new
SomeThirdParty(jQuery), new Http(new Thing())), new
Something());
//....
var user = new User(new SessionStore(new
SomeThirdParty(jQuery), new Http(new Thing())), new
Something());
//....
var user = new User(new SessionStore(new
SomeThirdParty(jQuery), new Http(new Thing())), new
Something());
Monday, September 9, 13
17. DI Container (e.g. Injector, Provider)
• Instantiates objects and their dependencies on demand.
• Allows to configure objects before instantiation.
• Can instantiate new objects on demand or provide existing
ones from cache.
• The objects must never know they are being managed by the
container.
• A container should be able to manage any object.
Monday, September 9, 13
19. How The DI Injector Works?!
• Angular inject the requested service by the function argument
names.
• Can also be done with an array.
• Once requested Angular’s injector would instantiate the
requested service and inject it.
angular.module('myModule')
.controller('MyCtrl', MyCtrl);
function MyCtrl($http) {
$http.get('http://google.com').then(getTheMonkey);
}
Monday, September 9, 13
20. The Array Notation
• Allows minifiers to preserve argument names for the
dependency injection to work with.
• More flexible - Separates dependency declaration from your
unit.
angular.module('myModule')
.controller('MyCtrl', ['$http', MyCtrl]);
function MyCtrl($http) {
$http.get('http://google.com').then(getTheMonkey);
}
Monday, September 9, 13
21. Changing The Implementation
angular.module('myModule')
.controller('MyCtrl', ['myHttp', MyCtrl])
.factory('myHttp', ['$q', myHttp]);
function myHttp($q) {
return {
get: function() {
var defer = $q.defer();
// Do something with XHR and return a promise...
return defer.promise;
}
};
}
function MyCtrl($http) {
$http.get('http://google.com').then(someCallback);
}
• Changes the implementation by changing only the array
notation.
• Angular’s injector instantiates the dependencies of each
dependency.
Monday, September 9, 13
23. Service/Factory
A service is the Angular way of exposing objects within the
$injector.
• Can have multiple dependencies that will be injected when
invoked.
angular.module('myModule')
.service('myHttp', ['$q', myHttp]);
function myHttp($q) {
this.get = function() {
var defer = $q.defer();
// Do something with XHR and return a promise...
return defer.promise;
};
}
Monday, September 9, 13
24. Factory
Shorthand for registering services without a constructor
function (assigned to $get property directly).
angular.module('myModule')
.factory('myHttp', ['$q', myHttp]);
function myHttp($q) {
return {
get: function() {
var defer = $q.defer();
// Do something with XHR and return a promise...
return defer.promise;
}
};
}
Monday, September 9, 13
25. Provider
Registers a provider to a service.
• Allows the save configuration state to the service.
• Only constants can be injected.
• The provider is a constructor function.
• $get is a function that returns the actual service.
Monday, September 9, 13
27. Configuration Phase vs. Run Phase
Configuration Phase
Run Phase
• Runs before any service was instantiated.
• Only providers can be injected.
• Each provider is injected with the “Provider” suffix (e.g.
$locationProvider)
• Allows to purely configure the services state.
• Services state should be not be changed now (already
configured during run phase).
• Providers now cannot be injected.
Monday, September 9, 13
28. Config
Use the service providers to configure the services state
during the config phase to the run phase.
angular.module('myModule')
.config(['myHttpProvider', '$locationProvider', appConfig]);
function appConfig(myHttpProvider, $locationProvider) {
// Configure app to use HTML5 History API..
$locationProvider.html5Mode(true);
// Configure my service baseUrl..
myHttpProvider.baseUrl('http://www.example.com');
}
Monday, September 9, 13
29. Value
Value is a shorthand to register a simple value as a service.
angular.module('myModule')
.value('myHttp', 'some string');
Monday, September 9, 13
30. Constant
Constant is the same as value, but unlike value it can be
injected to configuration function
angular.module('myModule')
.constant('someConstant', '123');
Monday, September 9, 13
31. Angular’s DI In Testing
describe('myHttp', function() {
var mockQ = {
then: function(){}
},
http;
beforeEach(module(function($provide) {
$provide.value('$q', mockQ);
}));
beforeEach(inject(function(myHttp) {
http = myHttp;
}));
describe('#get()', function() {
it('should return a promise', function() {
// test your code here
});
});
});
Monday, September 9, 13