Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Services and Dependency Injection

Angular has a bit of similarity to the .NET ecosystem in regards to handling services. The classes that provide some specific functionalities (like logging) can be brought into other classes (e.g. components) using Dependency Injection. Here’s a simple example:

Service
export class LoggerService {
log(message: string) {
console.log(message);
}
}
Component
@Component({
...,
providers: [LoggerService]
})
export class MyComponent {
constructor(private logger: LoggerService) {}
someMethod() {
this.logger.log('Something happened');
}
}

The highlighted lines show, in order:

  1. How to create a scope for LoggerService
  2. Constructor injection
  3. Using the injected service.

File Naming

Just like other kinds of entities in Angular, files containing services are usually named following the template of <name>.service.ts.

inject

We can also inject services with the inject function:

import { inject } from '@angular/core'
export class MyComponent {
const logger = inject(LoggerService);
someMethod() {
this.logger.log('Something happened');
}
}

Services in Angular are just typical TypeScript classes. They do not use any Angular-specific decorator (unless you want to inject it into some other service).

Scope

The way how we injected the service into the component in the example above has some consequences:

  • the child components (direct and indirect) will receive the same instance of the LoggerService (if they ask for it)
  • all the other components (other than the children of MyComponent) would receive different instance(s) of LoggerService

The Angular injector is hierarchical. We can inject services into:

  • components - providers of a module or component
  • services - providers of a module and @Injectable
  • directives
  • pipes

Singleton

Providing a service via SomeComponent makes it a singleton among the children tree of that component.

Providing a service via the AppComponent makes it a singleton among all components (since AppComponent is usually the root of any Angular app).

Providing a service in ANY eager-loaded module (e.g., AppModule), makes it a global singleton (for both all the components and services). T

Providing a service in a lazy-loaded module, makes that service available in entities declared within that module only. If some service is provided both globally and in lazy-loaded module, the lazy-loaded module (and its entities) will get its own instance of that service.

We can also decide that some service should be a singleton on this service’s level via the @Injectable({provideIn: 'root'}) decorator. Then, we don’t have to put it in any providers array.

This method cannot be used with interceptors. They have to be provided in a module.


The way how Angular sets up DI is different from the .NET’s way of doing that. In .NET we decide whether the service should be a singleton or not at the application root level. In Angular, each class/component may decide whether it wants to reuse some service instance or to get a new one.

Overriding

An instance of the service being injected (according to the rules above) may be overridden by a different instance of that service if we decide to provide it again.

The class that requires some service, but does not provide it, will receive the instance that was provided up in the tree (assuming that it was provided there).

Injecting into Services

Services may be injected into other services. Services do not use decorators by default, so there is no providers array. Instead, services that want to have some dependency injected, need to use the special @Injectable decorator.

Here’s an example:

@Injectable()
export class DataService {
constructor(private logger: Logger) {}
doSomething: Promise<Something> {
this.logger.log("Doing something");
}
}

Angular will try to match all the arguments that are required by the constructor with DI. In this case, Logger would be injected.

In order for a service to be injectable, it has to be provided in some module or the decorator should be used like this: @Injectable(provideIn: 'root') (there are other provideIn options as well).

Provide Strategies

Until now, in the module’s providers array, we’d just list the names of the services. By default, the class’s name acts as a token that represents that class. Angular’s injector is more flexible and we can define a token and dependency to be different things. It’s a bit similar to how in .NET there’s an interface that classes depend on and a class that actually implements that interface and gets injected.

Providers can be also defined like this:

...
providers: [
// Create MyClass and inject in place of SomeToken
{ provide: SomeToken, useClass: MyClass },
// Inject whatever AnotherToken resolves to in place of SomeToken
{ provide: SomeToken, useExisting: AnotherToken },
// Returns a static value in place of SomeToken
{ provide: SomeToken, useValue: "abc" },
// Define a function that returns something in place of SomeToken
{
provide: SomeToken,
useFactory: () => something,
deps: [ ] // tokens to be used by the factory if it has some parameters to be injected
},
],

The factory injection function can be defined in a separate file, often called <service>.service.provider.ts.

Default

The default providers: [SomeService] shorthand can be treated as:

providers: [
{ provide: SomeService, useClass: SomeService }
],

Tokens

The injection tokens are recommended to be created like this:

import { InjectionToken } from '@angular/core';
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// and provide...
providers: [{ provide: APP_CONFIG, useValue: { a: 'b' } }]

Interfaces

Even though TypeScript has interfaces, and it would seem that they could be the perfect injection tokens, we can’t use them, because interfaces disappear during the transpilation process.

←  Directives
Routing  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.