Angular 14 dependency injection unlocked
Published: 2022-06-13 09:21:45

Quietly released with Angular 14 is a fix for this handy little `inject` utility  that allows us to create and retrieve dependencies from the injector tree without relying on parameterized class constructors ([read docs](https://angular.io/api/core/inject))

## A small change with big ramifications? 

![Angular changelog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4ku2xk9x1wmqrfcw3td.png)

Until now it's only been possible to use it in places such as an `InjectionToken` or `Injectable` constructor context. Angular 14 now allows `inject` to be used in `Directive` and `Component` constructor contexts as well.

This makes it a little nicer when injecting non class-based tokens.

```ts
const API = new InjectionToken<Api>("API")

// Angular <=13
@Directive()
export class MyDirective {
  constructor(@Inject(API) private api: Api) {}
}

// Angular <=13
@Component()
export class MyDirective {
  constructor(@Inject(API) private api: Api) {}
}

// Angular 14+
@Directive()
export class MyDirective {
  private api = inject(API) // type inferred
}

// Angular 14+
@Component()
export class MyDirective {
  private api = inject(API) // type inferred
}
```

## Higher Order Services

Another change in Angular 14 loosens the rules around abstract class constructors so we can safely use them without running afoul of the Angular compiler with `strictInjectionParameters`.

![Angular changelog](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gopciai1pogu11qhfeza.png)
 

Thanks to this change it's now much easier to compose higher order services using mixins. For example, we can replace the `ResourceManager` in [this example](https://dev.to/mmuscat/render-as-you-fetch-with-angular-lifecycle-hooks-396i) with service composition.

```ts
// generate higher order service, mixing plain value
// parameters with dependency injection
export function createResource<T extends Fetchable>(
  fetchable: Type<T>
): Type<Resource<T>> {
  @Injectable()
  class ResourceImpl extends Resource<T> {
    constructor() {
      super(inject(fetchable));
    }
  }
  return ResourceImpl;
}

@Injectable()
export abstract class Resource<T extends Fetchable> {
  // this value is injected
  private changeDetectorRef = inject(ChangeDetectorRef);
  private subscription = Subscription.EMPTY

  ...

  // Angular behaviors require the `Injectable` decorator
  ngOnDestroy() {
    this.subscription.unsubscribe()
  }
  
  // this value is passed in through `super()`
  constructor(private fetchable: Fetchable) {}
}
```

This example shows how we can now easily mix dependency injection with explicit parameter constructors while preserving Angular behaviors like `ngOnDestroy`.

Let's see it in action.

```ts
const endpoint = 'https://jsonplaceholder.typicode.com/todos'

@Injectable({ providedIn: 'root' })
export class FetchTodosByUserId implements Fetchable<Todo[]> {
  private http = inject(HttpClient);

  fetch(userId: string) {
    return this.http.get<Todo[]>(endpoint, {
      params: {
        userId,
      },
    });
  }
}
```

```html
<!-- todos.component.html -->
<div *ngFor="let todo of todos.value">
  <div>id: {{ todo.id }}</div>
  <div>title: {{ todo.title }}</div>
  <input disabled type="checkbox" [checked]="todo.completed" />
</div>
```

```ts
const TodosByUserId = createResource(FetchTodosByUserId);

@Component({
  selector: 'app-todos',
  templateUrl: './todos.component.html',
  providers: [TodosByUserId],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TodosComponent {
  protected todos = inject(TodosByUserId);

  @Input()
  userId: string;

  ngOnChanges() {
    this.todos.fetch(this.userId);
  }
}
```

A working example can be seen here 👉 [view on Stackblitz](https://stackblitz.com/edit/angular-ivy-g7skhf?file=src%2Fapp%2Ftodos.component.ts)

## A small step forward

Angular 14 has many long needed and welcomed improvements to the core developer experience. Be sure to check the [change log](https://github.com/angular/angular/blob/main/CHANGELOG.md#1400-2022-06-02) so you don't miss anything.

Happy Coding!