Hierarchical Dependency Injection in AngularJS

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

You have multiple instances of a component that need different service configurations.

<data-grid server="api.server1.com">...</data-grid>
<data-grid server="api.server2.com">...</data-grid>

Each grid needs its own DataService instance pointing to a different server. But AngularJS services are singletons. There’s only one DataService 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

Place a different instance higher in the tree to override:

<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.