Composons avec l'injection
Tags: angular, webdev, tutorial, news
Published: 2022-06-07 09:32:28

## Introduction

Avec la dernière version d'Angular, l'injection devient encore plus puissante.

A partir de la version 14, l'injection n'est plus uniquement réservée aux constructeurs de __classe__, elle pourra s'utiliser hors du contexte d'injection.

Cette nouveauté ouvre bien des possibilités, notamment la possibilité de créer des composables.

**Comment cela fonctionne ?**
**Comment l'intégrer dans nos applications ?**

La réponse à ces deux questions se trouve dans la nouvelle implémentation de la fonction __inject__.

## A quoi sert la fonction __inject__ ?

La fonction __inject__ permet d'injecter un "token", et par la même occasion d'en obtenir la référence, dans l'injecteur actif courant.

Dans les précédentes versions d'Angular, cette fonction ne pouvait s'utiliser uniquement dans le contexte de l'injection.

```ts
export const MY_HTTP = new InjectionToken('MY_HTTP', {
  provideIn: 'root',
  useFactory() {
    return inject(HttpClient);
  }
})

@Injectable()
export class TodoService {
  http = inject(MY_HTTP);
}
```

## Et maintenant ?

A partir de la version 14 d'Angular, cette fonction __inject__ va pouvoir s'utiliser hors du contexte d'injection à condition que cette dernière soit utilisée au moment:

- de la construction d'une classe
- pour initialiser un paramètre de cette même classe.

Pour faire simple, cette fonction va pouvoir s'utiliser dans les composants, directives et pipes.

```typescript
@Component({
  selector: 'app-todo',
  templateUrl: './todo.component.html',
  styleUrls: './todo.component.less'
})
export class TodoComponent {
  todos$ = inject(TodoService).getTodos();
}
```

## La composition

Avec cette nouvelle manière de faire, écrire des fonctions à fortes réutilisabilités devient très facile.

Pour les développeurs Vue, cette nouveauté peut s'apparenter à la composition.

```typescript
// params.helper.ts
export function getParam<T>(key: string): Observable<T> {
  const route = inject(ActivatedRoute);
  return route.paramMap.pipe(
    map(params => params.get(key)),
    distinctUntilChanged()
  );
}
```

```typescript
// todo-details.component.ts
@Component({
  selector: 'app-todo-details',
  templateUrl: './todo-details.component.html',
  styleUrls: './todo-details.component.less'
})
export class TodoComponent {
  todoId$ = getParam<Todo>('id');
  todo$ = todoId$.pipe(
    switchMap(id => inject(TodoService).getTodo(id))
  );
}
```


Un autre cas d'utilisation très pratique est la destruction des observables quand le composant est détruit.

Pour réaliser notre logique de destruction, Angular nous propose le hook __OnDestroy__ présent dans le __ViewRef__.

Il devient dès lors très facile d'écrire une logique générique de destruction d'observables.

```typescript
// clean-observable.helper.ts
export function untilDestroyed() {
  const subject = new Subject<void>();

  const viewRef = inject(ChangeDetectorRef) as ViewRef;

  viewRef.onDestroy(() => {
    subject.next();
    subject.complete()
  });

  return takeUntil(subject.asObservable())
}
```

```typescript
// todo-details.component.ts
@Component({
  selector: 'app-todo-details',
  templateUrl: './todo-details.component.html',
  styleUrls: './todo-details.component.less'
})
export class TodoComponent {
  unsubscribe$ = untilDestroyed();
  refreshDetails$ = new Subject<void>();
  ngOnInit(): void {
    this.refreshDetails$.pipe(unsubscribe$).subscribe();
  }
}
```

## Conclusion

Cette nouvelle fonctionnalité est sans nul doute très puissante et les possibilités d'utilisations et de compositions sont quasiment sans limites mais elle possède aussi des inconvénients.

Premièrement, cette nouveauté ne peut s'utiliser uniquement lors de la construction de composants. Cela a pour impact que l'utilisation des paramètres d'entrées **Input** d'un composant sont impossibles. Toutefois un workaround est possible en utilisant les closures, mais je le déconseille fortement.

Deuxièmement, les composants vont être plus compliqués à tester. En effet les mocks vont être difficiles à écrire.