> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Composons avec l'injection
- URL: https://www.sfeir.dev/front/composons-avec-linjection/
- Published: 2023-05-05T09:22:00.000Z
- Updated: 2023-05-12T14:51:28.000Z
- Description: La dernière version d'Angular étend l'utilisation de l'injection, facilitant la création de composables mais complexifiant les tests.
- Author: Nicolas Frizzarin
- Tags: Angular, Front

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

```typescript
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
export function getParam<T>(key: string): Observable<T> {
  const route = inject(ActivatedRoute);
  return route.paramMap.pipe(
    map(params => params.get(key)),
    distinctUntilChanged()
  );
}
```

```typescript
@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
export function destroyed() {
  const replaySubject = new replaySubject(1);

  inject(DestroyRef).onDestroy(() => {
    replaySubject.next(true);
    replaySubject.complete();
  });

  return <T>() => takeUntil<T>(replaySubject.asObservable());
}
```

```typescript
@Component({
  selector: 'app-todo-details',
  templateUrl: './todo-details.component.html',
  styleUrls: './todo-details.component.less'
})
export class TodoComponent {
  unsubscribe$ = destroyed();
  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.