An injection context is the Angular runtime scope in which dependency injection can resolve a requested token through the active injector. Contexts differ as Angular creates components, directives, services, and other DI-managed classes, allowing the same token to resolve through different injectors when appropriate. section 3.1.4
The `inject` function does not make dependency injection available everywhere. It works during DI-managed class creation, field initialization, provider `useFactory` functions, `InjectionToken` factories, and functions called from a constructor’s active call stack. A class method such as `ngOnInit` runs after creation, outside that context, so calling `inject` there fails. section 3.1.4 section 3.2.2
Use the boundary deliberately:
- Prefer field initializers with `inject` to remove constructors used only for dependency parameters. section 3.2.3
- Keep helper functions that call `inject` on a creation-time path; do not invoke them later from arbitrary methods. section 3.2.2
- When an API can accept a dependency explicitly, pass `DestroyRef` to use it outside an injection context. section 5.3.4
- Set up a testing injector before directly constructing a service that uses `inject`. section 8.3.4
Context-aware authentication state
A standalone footer initializes `isAuth$` through an `isAuth()` helper that injects `AuthService`. This works because the field initializer runs while Angular creates the component. Moving the same call into `ngOnInit` would place it outside the injection context and produce an error. section 3.2.2
Listing 5.8 Using takeUntilDestroyed outside injection context
export class EditEmployeeComponent implements OnInit {
permissionsService = inject(PermissionsService);
destroyRef = inject(DestroyRef); #1
form = new FormGroup<EmployeeForm>({
firstName: new FormControl('', {
nonNullable: true,
validators: [Validators.required],
}),
lastName: new FormControl('', { nonNullable: true }),
email: new FormControl('', { nonNullable: true }),
position: new FormControl('', { nonNullable: true }),
level: new FormControl('', { nonNullable: true }),
});
ngOnInit() { #2
this.permissionsService.hasPermission('EditEmployeePrivateDetails').pipe(
takeUntilDestroyed(this.destroyRef), #3
).subscribe(hasPermission => {
if (!hasPermission) {
this.form.controls.firstName.disable();
this.form.controls.lastName.disable();
this.form.controls.email.disable();
} else {
this.form.controls.firstName.enable();
this.form.controls.lastName.enable();
this.form.controls.email.enable();
}
});
}
}
This component retains `DestroyRef` from its creation-time context, then passes it to `takeUntilDestroyed` inside `ngOnInit`, where implicit injection is unavailable. section 5.3.4
Injection contexts can be confusing because `inject` looks like an ordinary function while depending on runtime timing. Unit tests that call `new AuthService()` bypass Angular’s context; `TestBed.runInInjectionContext` supplies one after providers and test dependencies are configured. section 3.2.4 section 8.3.4
