TL;DR
AngularJS DI is flat - services are global singletons. Convert services to directives and use the require property for DOM-tree based hierarchical injection.
The Problem
Different parts of the page need different instances of the same service.
<user-profile></user-profile>
<admin-panel></admin-panel>
Say user-profile should call the API with the user’s token, while admin-panel needs an admin token. Each needs its own AuthService instance. But AngularJS services are singletons. There’s only one AuthService for the entire app.
Why This Happens
AngularJS has a single, flat DI container. When you inject a service, you get the same instance everywhere.
Angular 2+ introduced hierarchical injectors. Each component can have its own injector that inherits from parent injectors. Child components can override services for their subtree.
AngularJS doesn’t have this. But we can fake it.
The Solution
Convert services to directives. Use the require property to look up the DOM tree.
Step 1: Create a service directive
angular.module('app').directive('authService', function() {
return {
restrict: 'A',
controller: function AuthServiceController() {
this.token = null;
this.setToken = function(token) {
this.token = token;
};
this.getHeaders = function() {
return { Authorization: 'Bearer ' + this.token };
};
}
};
});
Step 2: Place it in the DOM hierarchy
<div auth-service>
<user-profile></user-profile>
<user-settings></user-settings>
</div>
Step 3: Require it from child components
angular.module('app').directive('userProfile', function() {
return {
restrict: 'E',
require: '^authService',
link: function(scope, element, attrs, authService) {
var headers = authService.getHeaders();
// Use headers for API calls
}
};
});
The ^ prefix tells AngularJS to search up the DOM tree for the directive.
Hierarchical Override
Nest another instance deeper in the tree to override it for that subtree:
<div auth-service>
<!-- Uses parent auth -->
<user-profile></user-profile>
<div auth-service>
<!-- Uses its own auth -->
<admin-panel></admin-panel>
</div>
</div>
admin-panel gets the inner authService. user-profile gets the outer one.
Limitations
Only one directive per name. Unlike Angular 2, you can’t register multiple providers for the same token. The directive name is global.
Components must be directives. To use require, your component must be a directive. You can’t use this pattern with plain controllers.
No internal declaration. A component can’t declare a service directive and require it at the same time. The service must come from a parent.
Timing issues. If you pass configuration via attributes, the values might not be available when the directive links. Use $observe or move to controller with bindToController.
When to Use This
This technique works when:
- You need multiple isolated subtrees with different service instances
- The hierarchy naturally maps to your DOM structure
- You’re already using directives heavily
It’s awkward when:
- Service relationships don’t match DOM structure
- You need to share services across disconnected parts of the app
- You’re mixing with other state management approaches
Verdict
Hierarchical DI in AngularJS is a workaround, not a first-class feature. It solves specific problems but introduces complexity. Evaluate whether simpler approaches (factory functions, explicit configuration) work for your case first.