Skip to content

Commit 6e35038

Browse files
committed
Update RxJS chapter and exercices
1 parent b754e93 commit 6e35038

File tree

2 files changed

+74
-70
lines changed

2 files changed

+74
-70
lines changed

docs/src/fr/rxjs/README.md

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Le chapitre précédent vous a montré l'utilisation de base des Observables. Vo
2929

3030
Tout d'abord, illustrons les deuxième et troisième points :
3131

32-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-observable-training?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1"></iframe>
32+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-observable/tree/master?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1&title=Observable"></iframe>
3333

3434
L'`Observable` déclenche 3 notifications next suivies d'une notification complete. Un Observable arrête d'émettre des valeurs parce qu'elles sont erronées (callback error) ou parce qu'elles se terminent (callback complete). Les deux événements s'excluent mutuellement.
3535

@@ -70,15 +70,15 @@ fromEvent(document, 'click').subscribe({ next: _ => console.log('Clicked!') })
7070

7171
Le Stackblitz suivant vous permet de jouer avec ces exemples :
7272

73-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-observable-creation-training?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1"></iframe>
73+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-observable-creation/tree/master?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&title=Observable%20creation"></iframe>
7474

7575
## Filtrage et mapping
7676

7777
Semblable à la fonction bien connue `Array.prototype.map`, l'opérateur `map` ([marble](https://rxmarbles.com/#map) / [documentation](https://rxjs.dev/api/operators/map)) applique une projection à chaque valeur et émet cette projection dans l'`Observable` de sortie.
7878

7979
Transformons l'exemple précédent concernant l'événement click sur le document pour qu'il imprime les coordonnées du clic :
8080

81-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-observable-mapping?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1"></iframe>
81+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-rx-operators/tree/master?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1&title=Mapping%20and%20Filtering"></iframe>
8282

8383
::: tip Pipe
8484
`pipe()` est une fonction utilisée pour composer des opérateurs tels que `map()`, `filter()`, `take()`... Les opérateurs sont appliqués au flux dans l'ordre où ils sont passés à la fonction pipe
@@ -144,12 +144,12 @@ Les flux peuvent être composés à de nombreuses fins. Pour étudier cette noti
144144

145145
Il est assez courant de devoir enchaîner les appels backend. Par exemple, l'utilisateur vient de modifier une ressource et vous souhaitez que votre page affiche ses détails mis à jour. Certains backends renvoient les détails de la ressource mise à jour dans le corps de la réponse à l'appel d'édition. Cependant, certains renvoient simplement une réponse HTTP 200 ou 204 sans corps. Cela signifie que l'appel de modification et l'appel de détail doivent être chaînés pour mettre à jour l'interface utilisateur. RxJS fournit plusieurs opérateurs pour enchaîner les événements de manière déclarative. Nous utiliserons l'opérateur `switchMap` ([documentation](https://rxjs.dev/api/operators/switchMap) / [marble](https://rxmarbles.com/#switchMap)) dans ce cas. Vous pouvez l'essayer dans le Stackblitz ci-dessous (cliquez n'importe où sur l'aperçu et voyez ce qu'il se passe dans la console, cliquez à nouveau et voyez comment les choses changent dans la console).
146146

147-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/switchmap-training?ctl=1&devtoolsheight=33&embed=1&file=index.ts&hideExplorer=1&hideNavigation=1"></iframe>
147+
<iframe height='500' width='100%' src="https://stackblitz.com/edit/switchmap-training?ctl=1&devtoolsheight=33&embed=1&file=index.ts&hideExplorer=1&hideNavigation=1&title=SwitchMap%20exemple"></iframe>
148148

149149
**Question : A partir de cet exemple, qu'apprenez-vous sur le fonctionnement de switchMap ? (Regarder le diagramme marble peut aider)**
150150
Adaptons l'exemple ci-dessus au contexte des appels backend chaînés :
151151

152-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-chaining-observables?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1"></iframe>
152+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-switchMap/tree/master?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1&title=SwitchMap%20exercise"></iframe>
153153

154154
Un autre opérateur utile pour combiner les appels est `exhaustMap` ([documentation](https://rxjs.dev/api/operators/exhaustMap)). Alors que `switchMap` annule l'abonnement à l'Observable projeté précédent, exhaustMap ignore les nouveaux événements tant que l'Observable projeté précédent n'est pas terminé.
155155

@@ -163,7 +163,7 @@ Pourquoi ne pas les mélanger ?
163163
Le plus souvent, on le fait sans s'en rendre compte. Par exemple, dans la callback next d'un subscribe, vous appelez une méthode qui a un subscribe. C'est de l'imbrication de subscribes.
164164

165165
Exemple de ce que vous ne devriez PAS faire :
166-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-subscribe-do-not-do-this?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1"></iframe>
166+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-switchMap/tree/nested-subscribe?ctl=1&embed=1&file=src/app/app.component.ts&hideExplorer=1&hideNavigation=1&title=Don't nest subscribes"></iframe>
167167
:::
168168

169169
## Unsubscribing
@@ -173,34 +173,26 @@ Pour le moment nous avons vu comment subscribe à un Observable. Pour éviter le
173173
Réutilisons notre exemple de routage précédent pour illustrer comment les fuites mémoire peuvent se produire. Un Observable interval est créé dans la méthode ngOnInit du composant de détails du livre.
174174
**Accédez aux détails d'un livre et regardez la console. Puis quittez la page et revenez. Que se passe-t-il dans la console ? Qu'est-ce que ça veut dire ?**
175175

176-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-why-unubscribe?ctl=1&embed=1&file=src/app/book-details/book-details.component.ts&hideExplorer=1&hideNavigation=1"></iframe>
176+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-routing/tree/memory-leak?ctl=1&embed=1&file=src/app/book-details/book-details.component.ts&hideExplorer=1&hideNavigation=1&title=Memory leak"></iframe>
177177

178178
Quand unsubscribe ? Si vous n'avez aucune certitude que l'`Observable` s'achèvera ou qu'il produira une erreur, vous devez unsubscribe manuellement. Le `HttpClient` complète toujours l'Observable qu'il renvoie après avoir reçu une réponse. Donc, théoriquement, si vous ne rencontrez que des Observables du `HttpClient`, vous n'avez pas à unsubscribe. Dans les autres cas, **soyez prudent et unsubscribe**.
179179

180-
Comment unsubscribe ? Il y a deux manières :
181-
- La méthode `subscribe` renvoie un objet `Subscription` qui dont la mémoire peut être désallouée en appelant la méthode unsubscribe sur celui-ci lorsque vous le souhaitez, généralement lorsque le composant dans lequel il réside est détruit.
182-
- En utilisant l'opérateur `takeUntil` ([marble](https://rxmarbles.com/#takeUntil) / [documentation](https://rxjs.dev/api/operators/takeUntil)) et un [`Subject` ](https://rxjs.dev/guide/subject) qui est un type spécial d'`Observable` sur lequel il est possible d'appeler les méthodes next(), error() et complete().
180+
Comment unsubscribe ? En utilisant l'opérateur `takeUntilDestroyed` et en lui passant un `destroyRef`.
183181

184-
La deuxième méthode est plus facile à maintenir lorsque votre base de code grandit, c'est donc celle que vous devriez privilégier.
182+
Corrigons la fuite de mémoire de l'exemple précédent.
185183

186-
Corrigons la fuite de mémoire de l'exemple précédent. Pour illustrer les deux techniques, l'Observable interval a également été ajouté au composant des détails de l'auteur :
187-
188-
<iframe height='500' width='100%' src="https://stackblitz.com/edit/angular-unubscribe-example?ctl=1&embed=1&file=src/app/book-details/book-details.component.ts&hideExplorer=1&hideNavigation=1"></iframe>
184+
<iframe height='500' width='100%' src="https://stackblitz.com/github/ocunidee/atpw-routing/tree/memory-leak-fix?ctl=1&embed=1&file=src/app/book-details/book-details.component.ts&hideExplorer=1&hideNavigation=1&title=takeUntilDestroyed"></iframe>
189185

190186
## Pipe async
191187

192-
Invoquer la méthode subscribe sur un `Observable` et enregistrer la valeur dans une propriété du composant n'est pas le seul moyen d'afficher les valeurs de l'`Observable`. Angular fournit un pipe auquel l'`Observable` peut être transmis directement.
188+
Invoquer la méthode subscribe sur un `Observable` et enregistrer la valeur dans une propriété du composant n'est pas le seul moyen d'afficher les valeurs de l'`Observable`. Angular fournit un pipe à appliquer directement sur un `Observable`.
193189

194190
<CodeGroup>
195191
<CodeGroupItem title="Component class">
196192

197193
```ts
198194
export class AppComponent {
199-
counter: Observable<number>
200-
201-
ngOnInit(): void {
202-
this.counter = interval(1000)
203-
}
195+
counter: Observable<number> = interval(1000)
204196
}
205197
```
206198
</CodeGroupItem>
@@ -213,40 +205,50 @@ export class AppComponent {
213205
</CodeGroupItem>
214206
</CodeGroup>
215207

216-
Pour les objets, une syntaxe alternative existe pour éviter d'utiliser de manière répétitive le pipe async pour accéder à chaque champ :
208+
Pour les objets, une syntaxe alternative existe pour éviter d'utiliser de manière répétitive le pipe async pour accéder à chaque champ. Sachant que chaque pipe `async` crée une souscription et trigger de nouveau l'exécution de l'`Observable`, c'est à éviter :
217209

218210
<CodeGroup>
219211
<CodeGroupItem title="Component template">
220212

221213
```html
214+
<!-- A EVITER -->
222215
<p>{{(user | async)?.firstName}}</p>
223216
<p>{{(user | async)?.lastName}}</p>
224217
<p>{{(user | async)?.age}}</p>
225218

226-
<!-- OR -->
227-
228-
<ng-container *ngIf="user | async as user">
219+
<!-- METHODE À PREFEREE -->
220+
@if (user | async as user) {
229221
<p>{{user.firstName}}</p>
230222
<p>{{user.lastName}}</p>
231223
<p>{{user.age}}</p>
232-
</ng-container>
224+
}
225+
226+
<!-- EN UTILISANT LA SYNTAXE @let -->
227+
@let unwrappedUser = user | async;
228+
<p>{{unwrappedUser?.firstName}}</p>
229+
<p>{{unwrappedUser?.lastName}}</p>
230+
<p>{{unwrappedUser?.age}}</p>
233231
```
234232
</CodeGroupItem>
235233
<CodeGroupItem title="Component class">
236234

237-
```ts
235+
```ts{2}
236+
@Component({
237+
selector: 'app-root',
238+
imports: [AsyncPipe],
239+
templateUrl: './app.component.html',
240+
styleUrl: './app.component.scss'
241+
})
238242
export class AppComponent {
239-
user: Observable<User>
240-
241-
ngOnInit(): void {
242-
this.user = interval(1000).map(_ => new User('John', 'Snow', 28))
243-
}
243+
user: Observable<User> = interval(1000).pipe(map(_ => new User('John', 'Snow', 28)))
244244
}
245245
246-
interface User {
247-
firstName: string
248-
lastName: string
249-
age: number
246+
class User {
247+
constructor(
248+
public firstName: string,
249+
public lastName: string,
250+
public age: number
251+
) {}
250252
}
251253
```
252254
</CodeGroupItem>

0 commit comments

Comments
 (0)