Dependency injection and the AngularTS injector
Dependency injection (DI) is the mechanism AngularTS uses to supply components with the services they need. Rather than constructing dependencies directly, you declare what you need by name and the injector finds, instantiates, and delivers each dependency. This keeps components decoupled, composable, and straightforward to test in isolation.
How the injector works
When you call angular.bootstrap() (or use the ng-app directive), AngularTS
calls createInjector() with your list of modules. The injector builds two
internal caches:
- Provider cache — holds provider objects, accessible during the config phase.
- Instance cache — holds fully constructed service singletons, built on first request.
When a service is requested via $injector.get('myService'), the injector looks
up myServiceProvider, calls its get method (injecting that method’s own
dependencies), caches the result, and returns it. Every later get returns the
same instance.
// Retrieve a service
const $http = injector.get('$http');
// Built-in token literals infer their registered service type.
const $machine = injector.get(angular.tokens.$machine);
// Custom services can provide an explicit result type.
const flags = injector.get<{ enabled: boolean }>('featureFlags');
// Check existence without throwing
if (injector.has('myOptionalService')) {
const svc = injector.get('myOptionalService');
}
// Invoke a function with injection
injector.invoke([
'$rootScope',
'$http',
function ($rootScope, $http) {
// both dependencies are injected automatically
},
]);
Applications that use the injector directly can declare their complete custom
service registry once. Built-in token inference remains available alongside the
application services, and invoke() and instantiate() infer their return
types.
interface AppServices {
auth: AuthService;
featureFlags: { enabled: boolean };
}
const appInjector: ng.InjectorService<AppServices> = injector;
const auth = appInjector.get('auth');
const answer = appInjector.invoke(() => 42); // number
const session = appInjector.instantiate(Session); // Session
Declaring dependencies
AngularTS reads dependency names from the function’s arguments — but argument names are erased by minifiers. The safe, minification-proof approach is array annotation: an array where every element except the last is a dependency name string, and the last element is the function.
Array annotation (recommended)
angular.createModule('myApp', []).controller('UserCtrl', [
'$scope',
'$http',
'userService',
function ($scope, $http, userService) {
$scope.users = [];
userService.getAll().then(({ data }) => {
$scope.users = data;
});
},
]);
$inject property
class UserCtrl {
static $inject = ['$scope', '$http', 'userService'];
constructor(
private $scope: ng.Scope,
private $http: ng.HttpService,
private userService: UserService,
) {
$scope.users = [];
userService.getAll().then(({ data }) => {
$scope.users = data;
});
}
}
angular.createModule('myApp', []).controller('UserCtrl', UserCtrl);
Note: The
$injectstatic property on a class is the TypeScript-idiomatic form of array annotation. AngularTS does not infer dependencies from function parameter names.
Explicit dependency annotation
Every dependency-bearing function must use array annotation or a static
$inject property. An unannotated function with parameters throws immediately.
This invariant makes dependency injection deterministic under minification.
Injectable types
value
A fixed JavaScript value registered with module.value(). It is not available
during the config phase.
constant
Like value, but available during the config phase and cannot be overridden by a decorator.
factory
A function whose return value becomes the singleton. Must not return
undefined.
service
A constructor function. Instantiated with new once; the instance is the
singleton.
provider
The most flexible type. An object with a get method. Can expose configuration
methods accessible during the config phase.
decorator
Wraps an existing service. Receives $delegate (the original) and returns a
replacement or augmented version.
The provider pattern
Providers give you a way to configure a service before any instance is created.
During the config phase the provider itself is injected (not the instance),
allowing configuration. During the run phase and everywhere else, the
instance produced by get is injected.
private prefix = 'Hello';
// Configuration method — callable during config phase
setPrefix(value: string) {
this.prefix = value;
}
// The injector calls get to produce the service instance
get = ['$log', ($log: ng.LogService) => {
const prefix = this.prefix;
return {
greet(name: string) {
$log.info(`${prefix}, ${name}!`);
},
};
}];
}
angular.createModule('myApp', [])
.provider('greeter', GreeterProvider)
.config(['greeterProvider', function (greeterProvider) {
// During config, the *provider* is injected — note the "Provider" suffix
greeterProvider.setPrefix('Greetings');
}])
.run(['greeter', function (greeter) {
// During run, the *instance* is injected
greeter.greet('world');
}]);
Info: During the config phase, inject a provider by appending
Providerto its name:greeterProviderfor thegreeterservice. This naming convention is enforced by AngularTS internally.
Public Injectable Contracts
ng.InjectionTokenMap is
the authoritative mapping from every public single-dollar token to its named
contract. InjectorService.get()
uses this map automatically:
const compile: ng.CompileService = $injector.get('$compile');
const rootScope: ng.RootScopeService = $injector.get('$rootScope');
const stateRegistry: ng.StateRegistryService = $injector.get('$stateRegistry');
const browserWindow: ng.WindowService = $injector.get('$window');
Framework-owned double-dollar tokens are internal implementation details and are
deliberately absent from PublicInjectionTokens,
InjectionTokenMap, and
the ng namespace.
Every
InjectionTokenMap[ value is a named ](../../../typedoc/interfaces/InjectionTokenMap.html)ng.\*contract exported through the generated TypeDoc entry point. Adding a public token without both its namespace contract and documentation export failsmake check.
Decorating existing services
A decorator intercepts an existing service and can replace, wrap, or augment it.
The original instance is available as $delegate.
.decorator('$log', ['$delegate', function ($delegate) {
const originalInfo = $delegate.info.bind($delegate);
$delegate.info = function (...args: any[]) {
originalInfo('[DECORATED]', ...args);
};
return $delegate;
}]);
Injecting into different artifact types
Controllers
Controllers receive $scope as their first dependency by convention, followed
by any other services. Use array annotation or $inject.
app.controller('DashboardCtrl', [
'$scope',
'$http',
function ($scope, $http) {
/* ... */
},
]);
Directives
Directive factories are injected like any other factory. The returned directive definition object is not itself injected.
app.directive('myWidget', [
'$http',
'dataService',
function ($http, dataService) {
return {
restrict: 'E',
link(scope) {
dataService.load().then((data) => {
scope.data = data;
});
},
};
},
]);
Filters
Filter factories are injectable. The returned filter function itself is not — it receives only the value and optional arguments from the template.
app.filter('truncate', [
'$log',
function ($log) {
return function (text: string, limit = 80) {
$log.info('truncating');
return text.length > limit ? text.slice(0, limit) + '…' : text;
};
},
]);
Config and run blocks
Framework configuration is a typed declarative object. Run blocks have access to runtime services.
app.config({
$http: {
defaults: {
headers: {
common: { 'X-App': 'myApp' },
},
},
},
});
// Run — services are available
app.run([
'$rootScope',
'$state',
function ($rootScope, $state) {
$rootScope.on('$stateChangeError', () => $state.go('error'));
},
]);
$injector API reference
| Method | Description |
|---|---|
$injector.get(token) | Returns the service instance for token. Throws if not found. |
$injector.has(token) | Returns true if a provider for token is registered. |
$injector.invoke(fn, self?, locals?) | Calls fn with injected arguments. Optionally overrides specific tokens with locals. |
$injector.instantiate(Type, locals?) | Instantiates a constructor with new, injecting its dependencies. |
$injector.annotate(fn) | Returns the array of dependency names for fn. |
$injector.loadNewModules(mods) | Loads additional modules into a running injector. |
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.