@dsivd/prestations-ng 19.2.0-beta.11 → 19.2.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/GOOD_PRACTICES.md CHANGED
@@ -1,103 +1,104 @@
1
- # Utilisation technique (DEV)
1
+ # Technical usage (DEV)
2
2
 
3
- > Ce guide décrit les bonnes pratiques pour développer une prestation avec `prestations-ng`.
4
- > Il a été mis à jour pour la version **v19+** : composants **standalone**, **signals** et
5
- > fonction **`inject`** (plus de modules, plus d'injection par constructeur). Depuis la **v19.2**,
6
- > prestations-ng est **zoneless** : les exemples tiennent l'état de vos composants dans des signaux,
7
- > ce qui fonctionne avec ou sans zone.js (voir [Application zoneless](good-practices#application_zoneless)).
3
+ > This guide describes the good practices to develop a prestation with `prestations-ng`.
4
+ > It has been updated for version **v19+**: **standalone** components, **signals** and the
5
+ > **`inject`** function (no more modules, no more constructor injection). Since **v19.2**,
6
+ > prestations-ng is **zoneless**: the examples hold the state of your components in signals,
7
+ > which works with or without zone.js (see [Zoneless application](good-practices#zoneless_application)).
8
8
  >
9
- > 📚 Pour aller plus loin :
9
+ > 📚 To go further:
10
10
  > [ANGULAR_SIGNALS.md](ANGULAR_SIGNALS.md) · [COMPONENTS.md](COMPONENTS.md) · [UPGRADING_V19.md](UPGRADING_V19.md)
11
11
 
12
- ## Sommaire
12
+ ## Table of contents
13
13
 
14
14
  - [Documentation](good-practices#documentation)
15
- - [Mise à jour de la librairie](good-practices#mise___jour_de_la_librairie)
16
- - [Accessibilité](good-practices#accessibilit_)
17
- - [Structure HTML](good-practices#structure_html)
18
- - [Le focus](good-practices#le_focus)
19
- - [La balise `<label>`](good-practices#la_balise__code__lt_label_gt___code_)
20
- - [La balise `<ul>` ou `<ol>`](good-practices#la_balise__code__lt_ul_gt___code__ou__code__lt_ol_gt___code_)
21
- - [La balise `<fieldset>` et `<legend>`](good-practices#la_balise__code__lt_fieldset_gt___code__et__code__lt_legend_gt___code_)
22
- - [Liseuse d'écran (afficher / masquer)](good-practices#liseuse_d__cran__afficher___masquer_)
23
- - [Une page prestations-ng](good-practices#une_page_prestations-ng)
24
- - [Application zoneless](good-practices#application_zoneless)
25
- - [Afficher un état asynchrone](good-practices#afficher_un__tat_asynchrone)
26
- - [Agir sur le DOM après le rendu](good-practices#agir_sur_le_dom_apr_s_le_rendu)
27
- - [Tester un composant zoneless](good-practices#tester_un_composant_zoneless)
28
- - [Le routage de vos pages](good-practices#le_routage_de_vos_pages)
29
- - [Formulaire simple](good-practices#formulaire_simple)
30
- - [Formulaire complexe (sous-routes)](good-practices#formulaire_complexe__sous-routes_)
31
- - [Création d'un composant réutilisable](good-practices#cr_ation_d_un_composant_r_utilisable)
32
- - [Exemple avec foehn-input-date de prestations-ng](good-practices#exemple_avec_foehn-input-date_de_prestations-ng)
33
- - [Afficher la validation globale du composant](good-practices#afficher_la_validation_globale_du_composant)
34
- - [Éviter des problèmes d'affichage d'erreurs dans une boucle `@for`](good-practices#_viter_des_probl_mes_d_affichage_d_erreurs_dans_une_boucle__code__for__code_)
35
- - [Validation du formulaire](good-practices#validation_du_formulaire)
36
- - [Gestion standard](good-practices#gestion_standard)
37
- - [Gestion manuelle](good-practices#gestion_manuelle)
15
+ - [Updating the library](good-practices#updating_the_library)
16
+ - [Accessibility](good-practices#accessibility)
17
+ - [HTML structure](good-practices#html_structure)
18
+ - [Focus](good-practices#focus)
19
+ - [The `<label>` tag](good-practices#the__code__lt_label_gt___code__tag)
20
+ - [The `<ul>` or `<ol>` tag](good-practices#the__code__lt_ul_gt___code__or__code__lt_ol_gt___code__tag)
21
+ - [The `<fieldset>` and `<legend>` tags](good-practices#the__code__lt_fieldset_gt___code__and__code__lt_legend_gt___code__tags)
22
+ - [Screen reader (show / hide)](good-practices#screen_reader__show___hide_)
23
+ - [A prestations-ng page](good-practices#a_prestations-ng_page)
24
+ - [Zoneless application](good-practices#zoneless_application)
25
+ - [Displaying an asynchronous state](good-practices#displaying_an_asynchronous_state)
26
+ - [Deriving a state from view queries](good-practices#deriving_a_state_from_view_queries)
27
+ - [Acting on the DOM after rendering](good-practices#acting_on_the_dom_after_rendering)
28
+ - [Testing a zoneless component](good-practices#testing_a_zoneless_component)
29
+ - [Routing your pages](good-practices#routing_your_pages)
30
+ - [Simple form](good-practices#simple_form)
31
+ - [Complex form (sub-routes)](good-practices#complex_form__sub-routes_)
32
+ - [Creating a reusable component](good-practices#creating_a_reusable_component)
33
+ - [Example with foehn-input-date of prestations-ng](good-practices#example_with_foehn-input-date_of_prestations-ng)
34
+ - [Displaying the global validation of the component](good-practices#displaying_the_global_validation_of_the_component)
35
+ - [Avoiding error display problems in a `@for` loop](good-practices#avoiding_error_display_problems_in_a__code__for__code__loop)
36
+ - [Form validation](good-practices#form_validation)
37
+ - [Standard handling](good-practices#standard_handling)
38
+ - [Manual handling](good-practices#manual_handling)
38
39
  - [Back-end](good-practices#back-end)
39
40
  - [Front-end](good-practices#front-end)
40
- - [Validation manuelle d'un composant](good-practices#validation_manuelle_d_un_composant)
41
- - [Cacher une erreur](good-practices#cacher_une_erreur)
42
- - [Afficher une erreur](good-practices#afficher_une_erreur)
41
+ - [Manual validation of a component](good-practices#manual_validation_of_a_component)
42
+ - [Hiding an error](good-practices#hiding_an_error)
43
+ - [Displaying an error](good-practices#displaying_an_error)
43
44
 
44
- > ⚠️ Dans toutes vos pages HTML vous devez avoir un `<foehn-form>` et celui-ci **ne peut pas être
45
- > conditionnel** (pas de `@if` autour de `<foehn-form>`).
45
+ > ⚠️ All your HTML pages must have a `<foehn-form>`, and it **can't be
46
+ > conditional** (no `@if` around `<foehn-form>`).
46
47
 
47
48
  ## Documentation
48
49
 
49
50
  - [PrestaKit](https://www.vd.ch/toutes-les-autorites/departements/departement-des-infrastructures-et-des-ressources-humaines-dirh/direction-generale-du-numerique-et-des-systemes-dinformation-dgnsi/prestakit-sdk-pour-le-developpement-des-prestations-en-ligne/)
50
51
  - [prestations-ng](https://dsi-vd.github.io/prestations-ng/)
51
- - [prestations-ng (doc autogénérée)](https://dsi-vd.github.io/prestations-ng/generateddoc/)
52
- - [foehn](https://dsi-vd.github.io/foehn-design-system/components/detail/formulaire-prestation--default.html) (Charte graphique)
52
+ - [prestations-ng (generated doc)](https://dsi-vd.github.io/prestations-ng/generateddoc/)
53
+ - [foehn](https://dsi-vd.github.io/foehn-design-system/components/detail/formulaire-prestation--default.html) (Graphic charter)
53
54
  - [CSS](https://www.w3schools.com/css/default.asp)
54
55
  - [Bootstrap](https://getbootstrap.com/docs/5.3/layout/grid/)
55
56
  - [Flex - Bootstrap](https://getbootstrap.com/docs/5.3/utilities/flex/)
56
57
  - [Flex](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
57
58
  - [HTML](https://www.w3schools.com/html/default.asp)
58
- - [Éléments de section](https://www.alsacreations.com/article/lire/1376-html5-section-article-nav-header-footer-aside.html)
59
+ - [Section elements](https://www.alsacreations.com/article/lire/1376-html5-section-article-nav-header-footer-aside.html) (in French)
59
60
  - [Description list](https://www.w3schools.com/tags/tag_dl.asp)
60
61
  - [RXJS](https://rxjs-dev.firebaseapp.com/)
61
62
 
62
- ## Mise à jour de la librairie
63
+ ## Updating the library
63
64
 
64
- > ℹ️ Maintenez à jour vos librairies en vous inscrivant sur la page [PrestaKit](https://www.vd.ch/toutes-les-autorites/departements/departement-des-infrastructures-et-des-ressources-humaines-dirh/direction-generale-du-numerique-et-des-systemes-dinformation-dgnsi/prestakit-sdk-pour-le-developpement-des-prestations-en-ligne/)
65
- > et vous serez ainsi notifié des nouvelles releases. Cela vous permettra de profiter des corrections
66
- > de bug, améliorations et nouvelles fonctionnalités.
65
+ > ℹ️ Keep your libraries up to date by subscribing on the [PrestaKit](https://www.vd.ch/toutes-les-autorites/departements/departement-des-infrastructures-et-des-ressources-humaines-dirh/direction-generale-du-numerique-et-des-systemes-dinformation-dgnsi/prestakit-sdk-pour-le-developpement-des-prestations-en-ligne/)
66
+ > page: you will be notified of the new releases. You will then benefit from the bug fixes,
67
+ > improvements and new features.
67
68
  >
68
- > Prenez le temps de lire le [changelog](CHANGELOG.md) de **prestations-ng** à chaque nouvelle release.
69
+ > Take the time to read the [changelog](CHANGELOG.md) of **prestations-ng** at each new release.
69
70
  >
70
- > N'hésitez pas à ouvrir des tickets JIRA (PRESTAKIT-xxx) dans [PrestaKit (anc SDK Cyber)](https://issuetracker.etat-de-vaud.ch/outils/issuetracker/browse/PRESTAKIT)
71
- > pour signaler des bugs ou même à faire des propositions d'améliorations afin de
72
- > [contribuer](CONTRIBUTING.md) au développement de prestations-ng.
71
+ > Don't hesitate to open JIRA tickets (PRESTAKIT-xxx) in [PrestaKit (formerly SDK Cyber)](https://issuetracker.etat-de-vaud.ch/outils/issuetracker/browse/PRESTAKIT)
72
+ > to report bugs, or even to suggest improvements in order to
73
+ > [contribute](CONTRIBUTING.md) to the development of prestations-ng.
73
74
 
74
- Mettre à jour la librairie prestations-ng (npm) :
75
+ Update the prestations-ng library (npm):
75
76
 
76
77
  ```bash
77
78
  ng update @dsivd/prestations-ng
78
79
  ```
79
80
 
80
- ## Accessibilité
81
+ ## Accessibility
81
82
 
82
- > ℹ️ En développant des prestations pour l'État, nous devons faire tout notre possible pour rendre nos
83
- > pages web les plus accessibles possibles au grand public.
83
+ > ℹ️ When developing prestations for the State, we must do everything we can to make our web pages
84
+ > as accessible as possible to the general public.
84
85
  >
85
- > Prestations-ng fait une bonne partie du boulot pour nous mais c'est au développeur de prendre le
86
- > soin de mettre les efforts qu'il faut pour que la prestation reste accessible.
86
+ > Prestations-ng does a good part of the job for us, but it's up to the developer to make the
87
+ > necessary efforts so that the prestation stays accessible.
87
88
 
88
- ### Structure HTML
89
+ ### HTML structure
89
90
 
90
- > ⚠️ Nous recommandons :
91
+ > ⚠️ We recommend to:
91
92
  >
92
- > - une seule balise h1 (`<h1>`)
93
- > - respecter la hiérarchie h1, h2, h3..
94
- > - regrouper vos informations dans des sections (`<section>`) si vous avez plusieurs h2 (`<h2>`)
95
- > - utiliser la classe CSS `col-md-8` de Bootstrap pour définir la largeur de votre formulaire
96
- > - organisez votre texte avec des paragraphes (`<p>`)
97
- > - utiliser les balises `<dl>`, `<dt>` et `<dd>` pour afficher des clés / valeurs (voir [foehn-recap-section](https://dsi-vd.github.io/prestations-ng/recap))
98
- > - utiliser [foehn-abbr](https://dsi-vd.github.io/prestations-ng/misc) pour les abréviations tant que possible et sinon la balise abbr (`<abbr>`)
93
+ > - have a single h1 tag (`<h1>`)
94
+ > - respect the hierarchy h1, h2, h3..
95
+ > - group your information in sections (`<section>`) if you have several h2 (`<h2>`)
96
+ > - use the Bootstrap CSS class `col-md-8` to define the width of your form
97
+ > - organize your text with paragraphs (`<p>`)
98
+ > - use the `<dl>`, `<dt>` and `<dd>` tags to display keys / values (see [foehn-recap-section](https://dsi-vd.github.io/prestations-ng/recap))
99
+ > - use [foehn-abbr](https://dsi-vd.github.io/prestations-ng/misc) for the abbreviations whenever possible, and the abbr tag (`<abbr>`) otherwise
99
100
 
100
- Ce qu'il ne faut **pas** faire :
101
+ What **not** to do:
101
102
 
102
103
  ```html
103
104
  <h1>...</h1>
@@ -107,7 +108,7 @@ Ce qu'il ne faut **pas** faire :
107
108
  ... ... ...
108
109
  ```
109
110
 
110
- Ce qu'il **faut** faire :
111
+ What **to** do:
111
112
 
112
113
  ```html
113
114
  <h1>...</h1>
@@ -122,27 +123,27 @@ Ce qu'il **faut** faire :
122
123
  <section>...</section>
123
124
  ```
124
125
 
125
- Quelques attributs HTML utiles :
126
+ Some useful HTML attributes:
126
127
 
127
- - Pour les images (`<img>`)
128
- - `title="[texte explicite ici]"`
129
- - `alt="[texte explicite ici]"`
130
- - Pour les liens (`<a>`)
131
- - `title="[texte explicite ici]"`
128
+ - For the images (`<img>`)
129
+ - `title="[explicit text here]"`
130
+ - `alt="[explicit text here]"`
131
+ - For the links (`<a>`)
132
+ - `title="[explicit text here]"`
132
133
  - `target="_blank"`
133
- - [Rendre les balises accessibles](https://www.w3.org/TR/wai-aria-1.2/#intro_ria_accessibility)
134
+ - [Make the tags accessible](https://www.w3.org/TR/wai-aria-1.2/#intro_ria_accessibility)
134
135
  - [`aria-*`](https://w3c.github.io/using-aria/#aria-states-and-properties-aria-attributes)
135
136
  - [`role`](https://www.w3.org/WAI/PF/HTML/wiki/RoleAttribute)
136
137
 
137
- ### Le focus
138
+ ### Focus
138
139
 
139
- > ⚠️ Il est important que le focus dans une page HTML soit maîtrisé. Si le focus est perdu, alors la
140
- > liseuse d'écran le sera aussi.
140
+ > ⚠️ It's important that the focus in an HTML page is under control. If the focus is lost, the
141
+ > screen reader is lost too.
141
142
  >
142
- > Si, par exemple, on ouvre une modale en cliquant sur un bouton, alors à la fermeture de la modale,
143
- > le focus doit être remis sur le bouton déclencheur.
143
+ > If, for example, a modal is opened by clicking on a button, then when the modal is closed, the
144
+ > focus must be put back on the triggering button.
144
145
 
145
- Exemple :
146
+ Example:
146
147
 
147
148
  ```html
148
149
  <button
@@ -151,7 +152,7 @@ Exemple :
151
152
  (click)="openModal()"
152
153
  #modalTrigger
153
154
  >
154
- <!-- on déclare le déclencheur de la modale -->
155
+ <!-- we declare the trigger of the modal -->
155
156
  Open Modal
156
157
  </button>
157
158
  <foehn-modal
@@ -160,13 +161,13 @@ Exemple :
160
161
  [(isModalVisible)]="isModalVisible"
161
162
  [modalTriggerHtmlElement]="modalTrigger"
162
163
  >
163
- <!-- on utilise le déclencheur -->
164
+ <!-- we use the trigger -->
164
165
  <div>Here is the dialog</div>
165
166
  </foehn-modal>
166
167
  ```
167
168
 
168
169
  ```ts
169
- // Un signal : la modale s'affiche et se masque aussi sans zone.js
170
+ // A signal: the modal is also displayed and hidden without zone.js
170
171
  readonly isModalVisible = signal(false);
171
172
 
172
173
  openModal(): void {
@@ -174,105 +175,106 @@ openModal(): void {
174
175
  }
175
176
  ```
176
177
 
177
- ### La balise `<label>`
178
+ ### The `<label>` tag
178
179
 
179
- > ⚠️ Tous nos composants `foehn-input-*` disposent d'une balise label ([`<label>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/label))
180
- > qui décrit le champ de saisie. Il est important que celle-ci soit remplie avec un contenu explicite.
180
+ > ⚠️ All our `foehn-input-*` components have a label tag ([`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label))
181
+ > which describes the input field. It's important that it is filled with an explicit content.
181
182
  >
182
- > Si pour une raison quelconque, le métier ne souhaite pas l'afficher, vous devez tout de même la
183
- > renseigner et ajouter un attribut `[isLabelSrOnly]="true"`. Le label ne sera pas affiché mais
184
- > uniquement lu par la liseuse d'écran.
183
+ > If for any reason the business doesn't want to display it, you must still fill it and add an
184
+ > `[isLabelSrOnly]="true"` attribute. The label won't be displayed, only read by the screen
185
+ > reader.
185
186
 
186
- Exemple :
187
+ Example:
187
188
 
188
189
  ```html
189
190
  <foehn-input-number
190
191
  name="..."
191
192
  [(model)]="..."
192
- [label]="'Le texte que la liseuse va lire'"
193
+ [label]="'The text read by the screen reader'"
193
194
  [isLabelSrOnly]="true"
194
195
  [required]="..."
195
196
  [maxlength]="..."
196
197
  />
197
198
  ```
198
199
 
199
- > 💡 `label`, `isLabelSrOnly`, `required`, ... sont désormais des **input signals** côté composant.
200
- > Vous continuez à les alimenter depuis le template parent via un binding `[label]="..."`.
201
- > À l'intérieur du composant, vous les lisez en les invoquant : `label()`, `required()`, ...
200
+ > 💡 `label`, `isLabelSrOnly`, `required`, ... are now **input signals** in the component.
201
+ > You keep feeding them from the parent template with a `[label]="..."` binding.
202
+ > Inside the component, you read them by calling them: `label()`, `required()`, ...
202
203
 
203
- ### La balise `<ul>` ou `<ol>`
204
+ ### The `<ul>` or `<ol>` tag
204
205
 
205
206
  > ⚠️
206
207
  >
207
- > - L'élément [`<ul>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ul) doit être utilisé
208
- > pour regrouper plusieurs éléments qui n'ont pas de relation d'ordre. Si on hésite entre
209
- > [`<ol>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ol) et
210
- > [`<ul>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ul), on se demandera si changer
211
- > l'ordre des éléments de la liste a un impact : si le déplacement d'un élément change la
212
- > signification, cela signifie que l'ordre est important et qu'il faudra utiliser
213
- > [`<ol>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ol), sinon l'ordre n'importe pas
214
- > et [`<ul>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ul) peut être utilisé.
215
- > - Si [`<ol>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ol) ou
216
- > [`<ul>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ul) ne contient qu'un seul élément
217
- > [`<li>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/li) (non dynamique), alors on se
218
- > demandera si l'utilisation d'une liste est nécessaire et si un paragraphe
219
- > ([`<p>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/p)) ne serait pas une meilleure
208
+ > - The [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) element must be used
209
+ > to group several items that have no order relationship. If you hesitate between
210
+ > [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) and
211
+ > [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul), ask yourself whether
212
+ > changing the order of the items of the list has an impact: if moving an item changes the
213
+ > meaning, the order is important and
214
+ > [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) must be used, otherwise
215
+ > the order doesn't matter and [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul)
216
+ > can be used.
217
+ > - If [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) or
218
+ > [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) only contains a single
219
+ > [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li) element (not dynamic),
220
+ > ask yourself whether a list is necessary and whether a paragraph
221
+ > ([`<p>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p)) wouldn't be a better
220
222
  > option.
221
- > - Ne **JAMAIS** mettre une autre balise que [`<li>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/li)
222
- > entre deux balises [`<ol>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ol) ou
223
- > [`<ul>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/ul).
223
+ > - **NEVER** put a tag other than [`<li>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li)
224
+ > between two [`<ol>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) or
225
+ > [`<ul>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul) tags.
224
226
 
225
- Exemple :
227
+ Example:
226
228
 
227
229
  ```html
228
230
  <ul>
229
- <li>un artichaut</li>
231
+ <li>an artichoke</li>
230
232
  <li>
231
- Les trucs pour le gâteau
232
- <!-- On voit que </li> n'est pas là -->
233
+ The things for the cake
234
+ <!-- Note that </li> isn't there -->
233
235
  <ul>
234
- <li>trois œufs</li>
236
+ <li>three eggs</li>
235
237
  <li>
236
- La génoise
237
- <!-- Là on ouvre une autre liste -->
238
+ The sponge cake
239
+ <!-- Here we open another list -->
238
240
  <ul>
239
- <li>100g de sucre</li>
240
- <li>un œuf</li>
241
- <li>150g de farine</li>
241
+ <li>100g of sugar</li>
242
+ <li>an egg</li>
243
+ <li>150g of flour</li>
242
244
  </ul>
243
245
  </li>
244
- <!-- On ferme la liste la plus imbriquée -->
245
- <li>200g de chocolat</li>
246
+ <!-- We close the most nested list -->
247
+ <li>200g of chocolate</li>
246
248
  </ul>
247
- <!-- On ferme la liste imbriquée avec </li> -->
249
+ <!-- We close the nested list with </li> -->
248
250
  </li>
249
- <li>De l'essuie-tout</li>
251
+ <li>Paper towels</li>
250
252
  <li>
251
- A faire dans l'ordre pour une belle vaisselle
253
+ To do in this order for a nice dishwashing
252
254
  <ol>
253
- <li>Remplir le lave vaisselle</li>
254
- <li>Mettre une pastille pour lave vaisselle</li>
255
- <li>Fermer la porte du lave vaisselle</li>
256
- <li>Lancer le lave vaisselle</li>
255
+ <li>Fill the dishwasher</li>
256
+ <li>Put a dishwasher tablet</li>
257
+ <li>Close the door of the dishwasher</li>
258
+ <li>Start the dishwasher</li>
257
259
  </ol>
258
260
  </li>
259
261
  </ul>
260
262
  ```
261
263
 
262
- ### La balise `<fieldset>` et `<legend>`
264
+ ### The `<fieldset>` and `<legend>` tags
263
265
 
264
- > ⚠️ L'élément HTML [`<fieldset>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/fieldset) est
265
- > utilisé afin de regrouper plusieurs contrôles interactifs ainsi que des étiquettes
266
- > ([`<label>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/label)) dans un formulaire HTML
267
- > et on doit lui associer une légende
268
- > ([`<legend>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/legend)) décrivant ce groupe.
266
+ > ⚠️ The HTML element [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset) is
267
+ > used to group several interactive controls as well as labels
268
+ > ([`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)) in an HTML form,
269
+ > and it must have a caption
270
+ > ([`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend)) describing this group.
269
271
  >
270
- > L'élément HTML [`<legend>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/legend) représente
271
- > une légende pour le contenu de son élément parent
272
- > [`<fieldset>`](https://developer.mozilla.org/fr/docs/Web/HTML/Element/fieldset). Il ne peut pas être
273
- > utilisé tout seul.
272
+ > The HTML element [`<legend>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend) represents
273
+ > a caption for the content of its parent element
274
+ > [`<fieldset>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset). It can't be
275
+ > used on its own.
274
276
 
275
- Exemple :
277
+ Example:
276
278
 
277
279
  ```html
278
280
  <form>
@@ -291,45 +293,45 @@ Exemple :
291
293
  </form>
292
294
  ```
293
295
 
294
- ### Liseuse d'écran (afficher / masquer)
296
+ ### Screen reader (show / hide)
295
297
 
296
- > ⚠️ Il est important de garder en tête qu'une liseuse d'écran va parcourir notre code HTML. De ce
297
- > fait, nous pouvons à tout moment décider de lui afficher explicitement du contenu à lire ou de lui
298
- > masquer du contenu pour ne pas surcharger l'information.
298
+ > ⚠️ It's important to keep in mind that a screen reader goes through our HTML code. So we can
299
+ > decide at any time to explicitly show it content to read, or to hide content from it so as not to
300
+ > overload the information.
299
301
 
300
- - Afficher uniquement pour la liseuse : utiliser la classe CSS `sr-only`
301
- - Masquer du contenu à la liseuse : utiliser l'attribut `aria-hidden="true"`
302
+ - Show only to the screen reader: use the CSS class `sr-only`
303
+ - Hide content from the screen reader: use the attribute `aria-hidden="true"`
302
304
 
303
- Exemple :
305
+ Example:
304
306
 
305
307
  ```html
306
308
  <a href="https://www.vd.ch">
307
309
  <span class="sr-only">
308
- <!-- Afficher uniquement pour la liseuse d'écran -->
309
- Retour à la page d'accueil
310
+ <!-- Show only to the screen reader -->
311
+ Back to the home page
310
312
  </span>
311
313
  <img
312
314
  aria-hidden="true"
313
315
  class="img-fluid footer-logo"
314
- src="[url_vers_mon_image]"
316
+ src="[url_to_my_image]"
315
317
  alt="Canton de Vaud"
316
318
  />
317
- <!-- Masquer uniquement pour la liseuse d'écran -->
319
+ <!-- Hide only from the screen reader -->
318
320
  </a>
319
321
  ```
320
322
 
321
- Le résultat de l'exemple ci-dessus sera un lien représenté par une image pour les voyants et un lien
322
- avec du texte pour les non-voyants.
323
+ The result of the example above is a link represented by an image for sighted people, and a link
324
+ with text for visually impaired people.
323
325
 
324
- ### Une page prestations-ng
326
+ ### A prestations-ng page
325
327
 
326
- 1. Étendre la classe abstraite `AbstractPageComponent` en spécifiant un modèle
327
- 2. Ajouter un titre h1 en utilisant `setPageTitle`
328
- 3. Implémenter la fonction `newForm`
329
- 4. Ajouter le `<foehn-form>` dans la page (⚠️ **il ne doit pas être conditionnel**)
330
- 5. La première balise d'en-tête de votre page doit être un h2 (`<h2>`)
331
- 6. Ajouter `<foehn-navigation>`
332
- 7. Si vous disposez de plusieurs pages, utiliser le composant `<foehn-page-counter>`
328
+ 1. Extend the abstract class `AbstractPageComponent`, specifying a model
329
+ 2. Add an h1 title with `setPageTitle`
330
+ 3. Implement the `newForm` function
331
+ 4. Add the `<foehn-form>` in the page (⚠️ **it must not be conditional**)
332
+ 5. The first heading tag of your page must be an h2 (`<h2>`)
333
+ 6. Add `<foehn-navigation>`
334
+ 7. If you have several pages, use the `<foehn-page-counter>` component
333
335
 
334
336
  `page-one.component.ts`
335
337
 
@@ -337,12 +339,12 @@ avec du texte pour les non-voyants.
337
339
  @Component({
338
340
  templateUrl: './page-one.component.html',
339
341
  imports: [
340
- // chaque composant importe ce dont il a besoin dans son template
342
+ // each component imports what its template needs
341
343
  FoehnFormComponent,
342
344
  FoehnNavigationComponent,
343
345
  FoehnPageCounterComponent,
344
346
  SdkDictionaryPipe,
345
- // ... vos composants foehn-* utilisés dans le template
347
+ // ... your foehn-* components used in the template
346
348
  ],
347
349
  })
348
350
  export class PageOneComponent
@@ -351,7 +353,7 @@ export class PageOneComponent
351
353
  {
352
354
  ngOnInit(): void {
353
355
  super.ngOnInit();
354
- // point (2) : ajoutera le titre h1
356
+ // point (2): adds the h1 title
355
357
  this.setPageTitle(this._dictionaryService.getKeySync('page.one.title'));
356
358
  }
357
359
 
@@ -362,28 +364,28 @@ export class PageOneComponent
362
364
  }
363
365
  ```
364
366
 
365
- > 💡 Plus de `constructor` : les dépendances de `AbstractPageComponent` (dont `_dictionaryService`)
366
- > sont injectées via `inject()` dans la classe abstraite. `activatedRoute` n'est donc plus un
367
- > paramètre de constructeur à passer.
367
+ > 💡 No more `constructor`: the dependencies of `AbstractPageComponent` (including `_dictionaryService`)
368
+ > are injected with `inject()` in the abstract class. `activatedRoute` is therefore no longer a
369
+ > constructor parameter to pass.
368
370
 
369
371
  `page-one.component.html`
370
372
 
371
373
  ```html
372
374
  <div class="container mt-5">
373
375
  <foehn-page-counter />
374
- <!-- point (7) : si besoin -->
376
+ <!-- point (7): if needed -->
375
377
 
376
- <!-- point (4) : doit toujours être présent dans la page et pas conditionnel (pas de @if) -->
378
+ <!-- point (4): must always be present in the page and not conditional (no @if) -->
377
379
  <foehn-form>
378
380
  <div class="row">
379
381
  <div class="col-md-8">
380
- <!-- point (5) : toujours avoir un <h2> en premier dans votre page -->
382
+ <!-- point (5): always have an <h2> first in your page -->
381
383
  <h2>
382
- {{ 'La_clé_du_dictionnaire_de_votre_titre_H2_ici' |
384
+ {{ 'The_dictionary_key_of_your_H2_title_here' |
383
385
  fromDictionary }}
384
386
  </h2>
385
387
 
386
- <!-- [VOS COMPOSANTS FOEHN ICI] -->
388
+ <!-- [YOUR FOEHN COMPONENTS HERE] -->
387
389
 
388
390
  <foehn-navigation
389
391
  id="navigation"
@@ -397,40 +399,40 @@ export class PageOneComponent
397
399
  </div>
398
400
  ```
399
401
 
400
- ## Application zoneless
402
+ ## Zoneless application
401
403
 
402
- > ℹ️ Depuis la v19.2, prestations-ng n'a plus besoin de zone.js et nous vous recommandons de rendre
403
- > votre application zoneless (`provideZonelessChangeDetection()`, voir
404
+ > ℹ️ Since v19.2, prestations-ng no longer needs zone.js and we recommend that you make your
405
+ > application zoneless (`provideZonelessChangeDetection()`, see
404
406
  > [UPGRADING_V19.md](UPGRADING_V19.md#zoneless-optional-but-recommended)).
405
407
  >
406
- > Sans zone.js, Angular ne rafraîchit un composant que lorsqu'il est prévenu d'un changement :
408
+ > Without zone.js, Angular only refreshes a component when it is notified of a change:
407
409
  >
408
- > - un listener de son template a été appelé (`(click)`, `(modelChange)`, `(userInput)`, ...), y
409
- > compris les double-bindings `[(model)]`
410
- > - un signal lu par son template a changé (`signal()`, `computed()`, `input()`, `model()`,
410
+ > - a listener of its template has been called (`(click)`, `(modelChange)`, `(userInput)`, ...),
411
+ > including the `[(model)]` two-way bindings
412
+ > - a signal read by its template has changed (`signal()`, `computed()`, `input()`, `model()`,
411
413
  > `toSignal()`, ...)
412
- > - `ChangeDetectorRef.markForCheck()` a été appelé, ce que fait le pipe `async` lorsqu'un observable
413
- > émet
414
+ > - `ChangeDetectorRef.markForCheck()` has been called, which is what the `async` pipe does when an
415
+ > observable emits
414
416
 
415
- ### Afficher un état asynchrone
417
+ ### Displaying an asynchronous state
416
418
 
417
- > ⚠️ Un champ modifié ailleurs (dans un `subscribe()`, un `setTimeout()`, une `Promise`, un
418
- > callback, ...) n'est plus affiché tant que rien d'autre ne rafraîchit le composant. Tenez-le dans
419
- > un **signal**.
419
+ > ⚠️ A field modified elsewhere (in a `subscribe()`, a `setTimeout()`, a `Promise`, a
420
+ > callback, ...) is no longer displayed until something else refreshes the component. Hold it in
421
+ > a **signal**.
420
422
 
421
- Ce qu'il ne faut **pas** faire :
423
+ What **not** to do:
422
424
 
423
425
  ```ts
424
426
  isLoading = true;
425
427
 
426
428
  ngOnInit(): void {
427
429
  this.myService.load().subscribe(() => {
428
- this.isLoading = false; // pas affiché sans zone.js
430
+ this.isLoading = false; // not displayed without zone.js
429
431
  });
430
432
  }
431
433
  ```
432
434
 
433
- Ce qu'il **faut** faire :
435
+ What **to** do:
434
436
 
435
437
  ```ts
436
438
  readonly isLoading = signal(true);
@@ -448,12 +450,12 @@ ngOnInit(): void {
448
450
  }
449
451
  ```
450
452
 
451
- > 💡 Pour un observable affiché tel quel, `toSignal(this.myService.load())` ou le pipe `async` font
452
- > le travail pour vous.
453
+ > 💡 For an observable displayed as is, `toSignal(this.myService.load())` or the `async` pipe do
454
+ > the job for you.
453
455
 
454
- Dans vos pages (`AbstractPageComponent`), prestations-ng rafraîchit la page lorsqu'il charge `form`
455
- et `reference`. Si vous modifiez vous-même le formulaire dans un callback asynchrone, appelez
456
- `markForCheck()` ensuite :
456
+ In your pages (`AbstractPageComponent`), prestations-ng refreshes the page when it loads `form`
457
+ and `reference`. If you modify the form yourself in an asynchronous callback, call
458
+ `markForCheck()` afterwards:
457
459
 
458
460
  ```ts
459
461
  private readonly changeDetectorRef = inject(ChangeDetectorRef);
@@ -461,16 +463,62 @@ private readonly changeDetectorRef = inject(ChangeDetectorRef);
461
463
  ngOnInit(): void {
462
464
  super.ngOnInit();
463
465
  this.sessionInfoWithApplicationService.prefillForm((info) => {
464
- this.form.email = info?.email ?? null;
465
- this.changeDetectorRef.markForCheck();
466
+ if (this.form) {
467
+ this.form.email = info?.email ?? null;
468
+ this.changeDetectorRef.markForCheck();
469
+ }
466
470
  });
467
471
  }
468
472
  ```
469
473
 
470
- ### Agir sur le DOM après le rendu
474
+ ### Deriving a state from view queries
475
+
476
+ > ⚠️ A field read by the template and modified in `ngAfterViewInit()`, `ngAfterContentInit()` or
477
+ > `ngAfterViewChecked()` is no longer displayed until something else refreshes the component (with
478
+ > zone.js, the next refresh displayed it, after an `NG0100` error in development).
479
+ > Compute it with `computed()` from `viewChild()` or `contentChild()`.
480
+
481
+ What **not** to do:
482
+
483
+ ```ts
484
+ @ViewChild('referenceLink') referenceLink?: TemplateRef<unknown>;
485
+ columns: FoehnTableColumnConfiguration<Order>[] = [];
486
+
487
+ ngAfterViewInit(): void {
488
+ // not displayed without zone.js
489
+ this.columns = [{ id: 'reference', columnLabelKey: 'orders.reference', template: this.referenceLink }];
490
+ }
491
+ ```
492
+
493
+ What **to** do:
494
+
495
+ ```ts
496
+ readonly referenceLink = viewChild<TemplateRef<unknown>>('referenceLink');
497
+ readonly columns = computed<FoehnTableColumnConfiguration<Order>[]>(() => [
498
+ { id: 'reference', columnLabelKey: 'orders.reference', template: this.referenceLink() },
499
+ ]);
500
+ ```
501
+
502
+ ```html
503
+ <foehn-table
504
+ [name]="'orders'"
505
+ [columnsConfiguration]="columns()"
506
+ [model]="orders()"
507
+ ></foehn-table>
508
+
509
+ <ng-template #referenceLink let-order="item">
510
+ <a [routerLink]="['/demande', order.id]">{{ order.reference }}</a>
511
+ </ng-template>
512
+ ```
513
+
514
+ > 💡 The `computed()` follows the result of the `viewChild()`, including when the element appears or
515
+ > disappears in an `@if`: no need to hide the content until it is resolved. If the element must
516
+ > always exist, `viewChild.required()` guarantees it (error `NG0951` otherwise).
517
+
518
+ ### Acting on the DOM after rendering
471
519
 
472
- > ⚠️ `NgZone` (`onStable`, `onMicrotaskEmpty`, `run()`, ...) ne fonctionne plus sans zone.js, et un
473
- > `setTimeout()` n'attend pas le rendu. Pour agir sur le DOM rendu (focus, scroll, ...), utilisez
520
+ > ⚠️ `NgZone` (`onStable`, `onMicrotaskEmpty`, `run()`, ...) no longer works without zone.js, and a
521
+ > `setTimeout()` doesn't wait for the rendering. To act on the rendered DOM (focus, scroll, ...), use
474
522
  > `afterNextRender()`.
475
523
 
476
524
  ```ts
@@ -481,18 +529,18 @@ private readonly injector = inject(Injector);
481
529
 
482
530
  showDetails(): void {
483
531
  this.detailsVisible.set(true);
484
- // Le champ n'existe qu'une fois le détail rendu
532
+ // The field only exists once the details are rendered
485
533
  afterNextRender(() => this.detailsField()?.nativeElement.focus(), {
486
534
  injector: this.injector,
487
535
  });
488
536
  }
489
537
  ```
490
538
 
491
- ### Tester un composant zoneless
539
+ ### Testing a zoneless component
492
540
 
493
- > ⚠️ `fixture.detectChanges()` **force** le rafraîchissement : un composant qui oublie de prévenir
494
- > Angular (champ au lieu d'un signal, `markForCheck()` manquant, ...) passe le test, mais n'est pas
495
- > rafraîchi dans l'application. Laissez Angular rafraîchir le composant, comme en production, avec
541
+ > ⚠️ `fixture.detectChanges()` **forces** the refresh: a component that forgets to notify
542
+ > Angular (a field instead of a signal, a missing `markForCheck()`, ...) passes the test, but isn't
543
+ > refreshed in the application. Let Angular refresh the component, as in production, with
496
544
  > `await fixture.whenStable()`.
497
545
 
498
546
  ```ts
@@ -504,22 +552,22 @@ it('should display the details', async () => {
504
552
  });
505
553
  ```
506
554
 
507
- `await fixture.whenStable()` attend aussi les tâches en cours, comme les requêtes HTTP : le test ne
508
- se termine pas avant elles (sinon `NG0205: Injector has already been destroyed`).
555
+ `await fixture.whenStable()` also waits for the pending tasks, such as the HTTP requests: the test
556
+ doesn't end before them (otherwise `NG0205: Injector has already been destroyed`).
509
557
 
510
- Gardez `fixture.detectChanges()` pour :
558
+ Keep `fixture.detectChanges()` for:
511
559
 
512
- - vérifier un état intermédiaire, avant la fin d'un traitement asynchrone
513
- - les tests avec `vi.useFakeTimers()` : le rafraîchissement d'Angular passe par des timers, qu'il
514
- faut avancer (`await vi.advanceTimersByTimeAsync(...)`), sinon `whenStable()` ne se termine pas
560
+ - checking an intermediate state, before the end of an asynchronous processing
561
+ - the tests with `vi.useFakeTimers()`: the refresh of Angular goes through timers, which must be
562
+ advanced (`await vi.advanceTimersByTimeAsync(...)`), otherwise `whenStable()` never ends
515
563
 
516
- > 💡 Pour que le test passe par le même chemin que l'utilisateur, déclenchez les événements dans le
517
- > DOM (`input.dispatchEvent(new Event('change'))`, `button.click()`, ...) plutôt que d'appeler les
518
- > méthodes du composant ou de ses services.
564
+ > 💡 So that the test goes through the same path as the user, trigger the events in the
565
+ > DOM (`input.dispatchEvent(new Event('change'))`, `button.click()`, ...) rather than calling the
566
+ > methods of the component or of its services.
519
567
 
520
- ## Le routage de vos pages
568
+ ## Routing your pages
521
569
 
522
- ### Formulaire simple
570
+ ### Simple form
523
571
 
524
572
  `app.routes.ts`
525
573
 
@@ -559,14 +607,14 @@ export const routes: Routes = [
559
607
  ];
560
608
  ```
561
609
 
562
- ### Formulaire complexe (sous-routes)
610
+ ### Complex form (sub-routes)
563
611
 
564
- > ℹ️ Préférez l'utilisation de **sous-routes** (`*.routes.ts`) pour regrouper vos routes et ainsi
565
- > mieux structurer votre projet.
612
+ > ℹ️ Prefer **sub-routes** (`*.routes.ts`) to group your routes and thus better structure your
613
+ > project.
566
614
  >
567
- > Comme il n'y a plus de modules (composants **standalone**), on ne charge plus un `*.module.ts` mais
568
- > un fichier de routes qui exporte une constante `Routes`. Voir [UPGRADING_V19.md](UPGRADING_V19.md)
569
- > pour le détail de la migration.
615
+ > As there are no more modules (**standalone** components), you no longer load a `*.module.ts` but
616
+ > a routes file exporting a `Routes` constant. See [UPGRADING_V19.md](UPGRADING_V19.md)
617
+ > for the details of the migration.
570
618
 
571
619
  `app.routes.ts`
572
620
 
@@ -606,9 +654,9 @@ export const routes: Routes = [
606
654
  ];
607
655
  ```
608
656
 
609
- > 💡 On charge donc `welcome.routes.ts` (et non plus `welcome.module.ts`) :
657
+ > 💡 So we load `welcome.routes.ts` (and no longer `welcome.module.ts`):
610
658
  > `loadChildren: () => import('./modules/welcome/welcome.routes').then((m) => m.welcome_routes)`
611
- > au lieu de `import('./modules/welcome/welcome.module').then((m) => m.WelcomeModule)`.
659
+ > instead of `import('./modules/welcome/welcome.module').then((m) => m.WelcomeModule)`.
612
660
 
613
661
  `welcome.routes.ts`
614
662
 
@@ -656,45 +704,44 @@ export const inscription_routes: Routes = [
656
704
  ];
657
705
  ```
658
706
 
659
- > 💡 Vous pouvez aussi charger paresseusement (`lazy-load`) un composant :
707
+ > 💡 You can also lazy-load a component:
660
708
  >
661
709
  > ```ts
662
710
  > { path: 'list', loadComponent: () => import('./demande-list-page/demande-list-page.component').then((m) => m.DemandeListPageComponent) },
663
711
  > ```
664
712
 
665
- ## Création d'un composant réutilisable
713
+ ## Creating a reusable component
666
714
 
667
- > ℹ️ Créer un composant réutilisable en Angular n'est pas difficile mais pour qu'il soit adapté à
668
- > prestations-ng, c'est une autre affaire. Il va falloir respecter certaines règles, sans quoi, ça ne
669
- > fonctionnera pas correctement.
715
+ > ℹ️ Creating a reusable component in Angular isn't difficult, but making it fit prestations-ng is
716
+ > another matter. Some rules must be respected, otherwise it won't work properly.
670
717
 
671
- ### Exemple avec foehn-input-date de prestations-ng
718
+ ### Example with foehn-input-date of prestations-ng
672
719
 
673
- Votre composant doit :
720
+ Your component must:
674
721
 
675
- 1. `selector` devrait commencer par `"app-..."` pour éviter d'éventuels conflits de nommage
676
- 2. avoir un `providers`
677
- 3. étendre `FoehnInputComponent` et vous devez lui préciser votre modèle de données
678
- 4. répartir les attributs de votre modèle de données dans des **signaux** publics utilisés dans
679
- votre HTML
680
- 5. implémenter une fonction `update` (ici ce sera `updateDate`) qui fera appel à `updateNgModel` pour
681
- maintenir la synchronisation de votre modèle de données avec votre page
682
- 6. implémenter une fonction `handleUserInput` qui fera appel à `handleChange` pour continuer à
683
- notifier le parent des changements de comportement de votre composant
684
- 7. implémenter une fonction `onModelChange` qui vous permettra d'initialiser correctement les
685
- signaux correspondant à votre modèle
686
- 8. implémenter une fonction `getValidValue` afin de reconstruire votre modèle de données avant de le
687
- retransmettre à votre page et retourner `null` si tous les attributs de votre objet sont vides (ça
688
- c'est pour que Java soit content)
722
+ 1. `selector` should start with `"app-..."` to avoid possible naming conflicts
723
+ 2. have a `providers`
724
+ 3. extend `FoehnInputComponent`, to which you must give your data model
725
+ 4. spread the attributes of your data model in public **signals** used in
726
+ your HTML
727
+ 5. implement an `update` function (here it will be `updateDate`) calling `updateNgModel` to
728
+ keep your data model synchronized with your page
729
+ 6. implement a `handleUserInput` function calling `handleChange` to keep
730
+ notifying the parent of the behavior changes of your component
731
+ 7. implement an `onModelChange` function to correctly initialize the
732
+ signals corresponding to your model
733
+ 8. implement a `getValidValue` function to rebuild your data model before sending it
734
+ back to your page, and return `null` if all the attributes of your object are empty (this
735
+ is to keep Java happy)
689
736
 
690
- > 🚨 Points **standalone / signals** à respecter :
737
+ > 🚨 **standalone / signals** points to respect:
691
738
  >
692
- > - **Pas de `multi: true`** dans le `providers` (retiré depuis la migration standalone).
693
- > - Les `imports` du composant se déclarent **directement** dans le décorateur `@Component`. Chaque
694
- > composant charge ce dont il a besoin (voir `foehn-input-date` dans ce projet).
695
- > - L'état affiché par le composant est tenu dans des **signaux** (point 4) : `onModelChange` est
696
- > appelé lorsque le modèle change, parfois en dehors d'un événement du template, et un simple champ
697
- > ne serait alors pas affiché sans zone.js.
739
+ > - **No `multi: true`** in the `providers` (removed since the standalone migration).
740
+ > - The `imports` of the component are declared **directly** in the `@Component` decorator. Each
741
+ > component loads what it needs (see `foehn-input-date` in this project).
742
+ > - The state displayed by the component is held in **signals** (point 4): `onModelChange` is
743
+ > called when the model changes, sometimes outside of an event of the template, and a plain field
744
+ > would then not be displayed without zone.js.
698
745
 
699
746
  `input-date.component.ts`
700
747
 
@@ -707,7 +754,7 @@ import { FoehnValidationAlertsComponent } from '../foehn-validation-alerts/foehn
707
754
  import { SdkDictionaryPipe } from '../sdk-dictionary/sdk-dictionary.pipe';
708
755
 
709
756
  @Component({
710
- // point (1) : dans votre prestation, le selector sera plutôt 'app-...'
757
+ // point (1): in your prestation, the selector will rather be 'app-...'
711
758
  selector: 'app-input-date',
712
759
  templateUrl: './input-date.component.html',
713
760
  // point (2)
@@ -715,10 +762,10 @@ import { SdkDictionaryPipe } from '../sdk-dictionary/sdk-dictionary.pipe';
715
762
  {
716
763
  provide: FoehnInputComponent,
717
764
  useExisting: forwardRef(() => InputDateComponent),
718
- // ⚠️ plus de `multi: true` en standalone
765
+ // ⚠️ no more `multi: true` in standalone
719
766
  },
720
767
  ],
721
- // les imports se déclarent directement ici : chaque composant charge ce dont il a besoin
768
+ // the imports are declared directly here: each component loads what it needs
722
769
  imports: [
723
770
  FoehnValidationAlertsComponent,
724
771
  FoehnInputNumberComponent,
@@ -726,9 +773,9 @@ import { SdkDictionaryPipe } from '../sdk-dictionary/sdk-dictionary.pipe';
726
773
  ],
727
774
  })
728
775
  export class InputDateComponent extends FoehnInputComponent<number[]> {
729
- // point (3) : extends FoehnInputComponent<number[]>
776
+ // point (3): extends FoehnInputComponent<number[]>
730
777
 
731
- // point (4) : les attributs de votre modèle répartis en signaux publics
778
+ // point (4): the attributes of your model spread in public signals
732
779
  readonly day = signal<string | null>(null);
733
780
  readonly month = signal<string | null>(null);
734
781
  readonly year = signal<string | null>(null);
@@ -787,17 +834,17 @@ export class InputDateComponent extends FoehnInputComponent<number[]> {
787
834
  }
788
835
  ```
789
836
 
790
- Chaque `foehn-input-*` utilisé dans votre vue doit :
837
+ Each `foehn-input-*` used in your view must:
791
838
 
792
- 1. avoir un `id` qui concatène l'appel à la fonction `buildId()` et le nom de l'attribut de votre objet
793
- 2. avoir un `name` qui concatène l'input **signal** `name()` et le nom de l'attribut de votre objet
794
- 3. avoir un `label`
795
- 4. utiliser votre signal public comme model en double-binding (banana in the box) : `[(model)]="day"`
796
- lit et écrit le signal (sans les parenthèses)
797
- 5. faire appel à `updateDate()` lors du déclenchement de l'événement `(modelChange)`
798
- 6. faire appel à `handleUserInput()` lors du déclenchement de l'événement `(userInput)`
839
+ 1. have an `id` concatenating the call to the `buildId()` function and the name of the attribute of your object
840
+ 2. have a `name` concatenating the **signal** input `name()` and the name of the attribute of your object
841
+ 3. have a `label`
842
+ 4. use your public signal as model in two-way binding (banana in the box): `[(model)]="day"`
843
+ reads and writes the signal (without the parentheses)
844
+ 5. call `updateDate()` when the `(modelChange)` event is triggered
845
+ 6. call `handleUserInput()` when the `(userInput)` event is triggered
799
846
 
800
- > 💡 `name` est désormais un **input signal**. On l'invoque : `name() + '_day'` (et non plus
847
+ > 💡 `name` is now an **input signal**. You call it: `name() + '_day'` (and no longer
801
848
  > `name + '_day'`).
802
849
 
803
850
  `input-date.component.html`
@@ -840,20 +887,20 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
840
887
  />
841
888
  ```
842
889
 
843
- ### Afficher la validation globale du composant
890
+ ### Displaying the global validation of the component
844
891
 
845
- - Il faut ajouter dans la partie HTML de votre composant le code décrit ci-dessous
846
- - Remonter une erreur correspondant au `name` de votre composant lorsque celui-ci est utilisé dans
847
- votre page de formulaire.
892
+ - Add the code below in the HTML part of your component
893
+ - Return an error matching the `name` of your component when it is used in
894
+ your form page.
848
895
 
849
- > 💡 Points **signals** à noter dans ce template :
896
+ > 💡 **signals** points to note in this template:
850
897
  >
851
- > - `@if` remplace `*ngIf` et les attributs du composant sont des signaux : on les invoque
898
+ > - `@if` replaces `*ngIf`, and the attributes of the component are signals: you call them
852
899
  > (`label()`, `required()`, `isLabelSrOnly()`, `hideNotRequiredExtraLabel()`, `helpText()`).
853
- > - `[ngClass]` est remplacé par des bindings `[class.xxx]="..."`.
900
+ > - `[ngClass]` is replaced by `[class.xxx]="..."` bindings.
854
901
 
855
902
  ```html
856
- <!-- (obligatoire) Affichera une barre verticale rouge sur l'entier du composant -->
903
+ <!-- (mandatory) Displays a red vertical bar on the whole component -->
857
904
  <div
858
905
  class="form-group"
859
906
  [class.has-danger]="hasErrorsToDisplay()"
@@ -861,12 +908,12 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
861
908
  [attr.id]="buildId('Container')"
862
909
  tabindex="-1"
863
910
  >
864
- <!-- (obligatoire) Permet de regrouper vos champs pour une validation globale -->
911
+ <!-- (mandatory) Groups your fields for a global validation -->
865
912
  <fieldset
866
913
  [attr.aria-describedby]="getDescribedBy()"
867
914
  [attr.aria-invalid]="hasErrorsToDisplay() || null"
868
915
  >
869
- <!-- (facultatif) Seulement si vous souhaitez afficher un label/legend global à votre composant -->
916
+ <!-- (optional) Only if you want to display a global label/legend for your component -->
870
917
  @if (!!label()) {
871
918
  <legend
872
919
  [class.visually-hidden]="isLabelSrOnly()"
@@ -881,10 +928,10 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
881
928
  </legend>
882
929
  }
883
930
 
884
- <!-- (obligatoire) -->
931
+ <!-- (mandatory) -->
885
932
  <foehn-validation-alerts [component]="this" />
886
933
 
887
- <!-- (facultatif) Seulement si vous souhaitez pouvoir afficher un helpText global au composant -->
934
+ <!-- (optional) Only if you want to be able to display a global helpText for the component -->
888
935
  @if (helpText()) {
889
936
  <small
890
937
  [attr.id]="buildId() + 'Help'"
@@ -893,36 +940,36 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
893
940
  ></small>
894
941
  }
895
942
 
896
- <!-- ...vos foehn-input-* ici -->
943
+ <!-- ...your foehn-input-* here -->
897
944
  </fieldset>
898
945
  </div>
899
946
  ```
900
947
 
901
- ### Éviter des problèmes d'affichage d'erreurs dans une boucle `@for`
948
+ ### Avoiding error display problems in a `@for` loop
902
949
 
903
- > ⚠️ Ne pas oublier de mettre un `track` avec un identifiant unique dans la boucle `@for` sinon la
904
- > validation ne s'affichera pas. Quand un identifiant unique tel que `id` n'est pas possible, tracker
905
- > par objet ou, en dernier recours, `track $index`.
950
+ > ⚠️ Don't forget to put a `track` with a unique identifier in the `@for` loop, otherwise the
951
+ > validation won't be displayed. When a unique identifier such as `id` isn't possible, track
952
+ > by object or, as a last resort, `track $index`.
906
953
 
907
- > ℹ️ Dans une boucle `@for` d'Angular avec un composant `FoehnInputComponent`, si vous validez votre
908
- > formulaire, les erreurs remontées sont synchronisées avec l'index de vos composants répétés dans la
909
- > boucle.
954
+ > ℹ️ In an Angular `@for` loop with a `FoehnInputComponent` component, if you validate your
955
+ > form, the errors returned are synchronized with the index of your components repeated in the
956
+ > loop.
910
957
  >
911
- > **Scénario :** Dans votre liste, lorsque vous supprimez un composant qui contient des erreurs à
912
- > l'index 0 (par ex.) et que le composant suivant (index 1) n'a pas d'erreur ou pas les mêmes
913
- > erreurs, alors ce dernier se retrouve avec les erreurs du composant fraîchement supprimé à
914
- > l'index 0.
958
+ > **Scenario:** In your list, when you remove a component that has errors at
959
+ > index 0 (for example) and the next component (index 1) has no errors or not the same
960
+ > errors, then the latter ends up with the errors of the component just removed at
961
+ > index 0.
915
962
  >
916
- > **Explication :** Ce phénomène est dû au fait que les erreurs remontées par GESDEM ne se
917
- > rafraîchissent que lors de la demande de sauvegarde des données. Du coup, le composant supprimé à
918
- > l'index 0 est remplacé par celui à l'index 1 mais les erreurs à l'index 0, elles, existent toujours
919
- > et se reportent donc sur le composant venant de l'index 1.
963
+ > **Explanation:** This happens because the errors returned by GESDEM are only
964
+ > refreshed when the data is saved. So the component removed at
965
+ > index 0 is replaced by the one at index 1, but the errors at index 0 still exist
966
+ > and are therefore carried over to the component coming from index 1.
920
967
 
921
- Voici la bonne pratique à mettre en place pour palier à ces problèmes d'affichage d'erreurs dans une
922
- boucle `@for`.
968
+ Here is the good practice to put in place to work around these error display problems in a
969
+ `@for` loop.
923
970
 
924
- Lorsque vous supprimez un élément dans votre liste, vous devez appeler une méthode se trouvant dans
925
- votre `AbstractPageComponent`. Cette méthode contiendra le code suivant :
971
+ When you remove an item from your list, you must call a method of
972
+ your `AbstractPageComponent`. This method will contain the following code:
926
973
 
927
974
  ```ts
928
975
  /*
@@ -932,30 +979,30 @@ votre `AbstractPageComponent`. Cette méthode contiendra le code suivant :
932
979
  this._gesdemService.validate(this.form).pipe(first()).subscribe();
933
980
  ```
934
981
 
935
- Voici un exemple de page de formulaire contenant cette méthode (1) :
982
+ Here is an example of a form page containing this method (1):
936
983
 
937
- > 💡 Plus de `constructor` : on fait de l'**inject**. Les dépendances de `AbstractPageComponent`
938
- > (dont `_dictionaryService` et `_gesdemService`) sont déjà injectées dans la classe abstraite. Pour
939
- > vos propres dépendances, utilisez `inject()`.
984
+ > 💡 No more `constructor`: we use **inject**. The dependencies of `AbstractPageComponent`
985
+ > (including `_dictionaryService` and `_gesdemService`) are already injected in the abstract class. For
986
+ > your own dependencies, use `inject()`.
940
987
 
941
988
  ```ts
942
989
  @Component({
943
- templateUrl: './votre-page.component.html',
990
+ templateUrl: './your-page.component.html',
944
991
  imports: [
945
- // ... vos composants foehn-*
992
+ // ... your foehn-* components
946
993
  ],
947
994
  })
948
- export class VotrePageComponent
995
+ export class YourPageComponent
949
996
  extends AbstractPageComponent<BusinessForm>
950
997
  implements OnInit
951
998
  {
952
- // vos propres dépendances : inject() au lieu du constructor
999
+ // your own dependencies: inject() instead of the constructor
953
1000
  private readonly metaService = inject(MetaService);
954
1001
 
955
1002
  ngOnInit(): void {
956
1003
  super.ngOnInit();
957
1004
  this.setPageTitle(
958
- this._dictionaryService.getKeySync('votre-page.title'),
1005
+ this._dictionaryService.getKeySync('your-page.title'),
959
1006
  );
960
1007
  }
961
1008
 
@@ -974,12 +1021,12 @@ export class VotrePageComponent
974
1021
  }
975
1022
  ```
976
1023
 
977
- Il ne tient qu'à vous de décider comment appeler cette méthode lors de la suppression de l'élément de
978
- votre liste.
1024
+ It's up to you to decide how to call this method when the item is removed from
1025
+ your list.
979
1026
 
980
- Voici une manière élégante de gérer vos listes d'éléments :
1027
+ Here is an elegant way to handle your lists of items:
981
1028
 
982
- - Structure des dossiers et fichiers
1029
+ - Structure of the folders and files
983
1030
 
984
1031
  ```text
985
1032
  demande-page/
@@ -996,7 +1043,7 @@ demande-page/
996
1043
 
997
1044
  **vehicule-item**
998
1045
 
999
- `vehicule-item.component.html` contient le bouton pour la suppression de l'élément dans la liste (1).
1046
+ `vehicule-item.component.html` contains the button removing the item from the list (1).
1000
1047
 
1001
1048
  ```html
1002
1049
  <div
@@ -1009,7 +1056,7 @@ demande-page/
1009
1056
  <foehn-validation-alerts [component]="this" />
1010
1057
 
1011
1058
  <div class="row d-flex align-items-end vehicule-infos">
1012
- <!-- ... vos composants foehn-input-* -->
1059
+ <!-- ... your foehn-input-* components -->
1013
1060
 
1014
1061
  <div class="col-md-2 mt-md-0 mt-xs-2">
1015
1062
  <button class="btn btn-danger" (click)="removeVehicule()">
@@ -1021,10 +1068,10 @@ demande-page/
1021
1068
  </div>
1022
1069
  ```
1023
1070
 
1024
- `vehicule-item.component.ts` contient la fonction de suppression qui va remonter l'événement à son
1025
- parent (la liste des éléments).
1071
+ `vehicule-item.component.ts` contains the removal function, which sends the event up to its
1072
+ parent (the list of items).
1026
1073
 
1027
- > 💡 `@Output() + EventEmitter` sont remplacés par la fonction `output()`.
1074
+ > 💡 `@Output() + EventEmitter` are replaced by the `output()` function.
1028
1075
 
1029
1076
  ```ts
1030
1077
  @Component({
@@ -1038,7 +1085,7 @@ parent (la liste des éléments).
1038
1085
  ],
1039
1086
  imports: [
1040
1087
  FoehnValidationAlertsComponent,
1041
- // ... vos foehn-input-*
1088
+ // ... your foehn-input-*
1042
1089
  SdkDictionaryPipe,
1043
1090
  ],
1044
1091
  })
@@ -1055,11 +1102,11 @@ export class VehiculeItemComponent extends FoehnInputComponent<Vehicule> {
1055
1102
 
1056
1103
  **vehicules-list**
1057
1104
 
1058
- `vehicules-list.component.html` contient votre boucle `@for` (1) et votre composant répétable (2) qui
1059
- devra lui aussi remonter l'événement.
1105
+ `vehicules-list.component.html` contains your `@for` loop (1) and your repeatable component (2), which
1106
+ must also send the event up.
1060
1107
 
1061
- > ⚠️ Dans la boucle `@for` ne pas oublier de mettre un `track` (identifiant unique) sinon la
1062
- > validation ne s'affichera pas. Ici nous trackons par exemple avec `vehicule.id`.
1108
+ > ⚠️ In the `@for` loop, don't forget to put a `track` (unique identifier), otherwise the
1109
+ > validation won't be displayed. Here, for example, we track with `vehicule.id`.
1063
1110
 
1064
1111
  ```html
1065
1112
  <div
@@ -1092,9 +1139,9 @@ devra lui aussi remonter l'événement.
1092
1139
  ></small>
1093
1140
  }
1094
1141
 
1095
- <!-- (1) votre boucle @for + track (identifiant unique) -->
1142
+ <!-- (1) your @for loop + track (unique identifier) -->
1096
1143
  @for (vehicule of vehicules(); track vehicule.id; let i = $index) {
1097
- <!-- (2) votre composant réutilisable -->
1144
+ <!-- (2) your reusable component -->
1098
1145
  <app-vehicule-item
1099
1146
  [name]="name() + '[' + i + ']'"
1100
1147
  [model]="vehicule"
@@ -1110,15 +1157,15 @@ devra lui aussi remonter l'événement.
1110
1157
  </div>
1111
1158
  ```
1112
1159
 
1113
- > 💡 Le `name` est un input signal : `[name]="name() + '[' + i + ']'"` (et non plus
1160
+ > 💡 The `name` is an input signal: `[name]="name() + '[' + i + ']'"` (and no longer
1114
1161
  > `[name]="name + '[' + i + ']'"`).
1115
1162
  >
1116
- > 💡 La variable d'une boucle `@for` ne peut pas être modifiée : on passe l'élément avec `[model]` et
1117
- > on remplace l'élément de la liste dans `updateVehiculesList()` (appelée par `(modelChange)`).
1163
+ > 💡 The variable of a `@for` loop can't be modified: the item is passed with `[model]`, and
1164
+ > the item of the list is replaced in `updateVehiculesList()` (called by `(modelChange)`).
1118
1165
 
1119
- `vehicules-list.component.ts` remonte à son tour l'événement (1) au parent (`AbstractPageComponent`)
1120
- ainsi que supprimer l'élément de la liste (2). C'est aussi ici que vous pouvez gérer l'ajout d'un
1121
- élément (3).
1166
+ `vehicules-list.component.ts` in turn sends the event (1) up to the parent (`AbstractPageComponent`)
1167
+ and removes the item from the list (2). This is also where you can handle the addition of an
1168
+ item (3).
1122
1169
 
1123
1170
  ```ts
1124
1171
  @Component({
@@ -1137,25 +1184,25 @@ ainsi que supprimer l'élément de la liste (2). C'est aussi ici que vous pouvez
1137
1184
  ],
1138
1185
  })
1139
1186
  export class VehiculesListComponent extends FoehnInputComponent<Vehicule[]> {
1140
- readonly removedVehicule = output<void>(); // (1) événement à remonter au parent
1187
+ readonly removedVehicule = output<void>(); // (1) event to send up to the parent
1141
1188
 
1142
- // Signal contenant vos éléments répétables : il est remplacé, jamais modifié sur place
1189
+ // Signal holding your repeatable items: it is replaced, never modified in place
1143
1190
  readonly vehicules = signal<Vehicule[]>([]);
1144
1191
 
1145
1192
  updateVehiculesList(vehicule: Vehicule, index: number): void {
1146
1193
  this.vehicules.update((vehicules) =>
1147
1194
  vehicules.map((item, i) => (i === index ? vehicule : item)),
1148
1195
  );
1149
- this.getValidValueAndUpdateNgModel(); // Mise à jour de votre modèle
1196
+ this.getValidValueAndUpdateNgModel(); // Update of your model
1150
1197
  }
1151
1198
 
1152
1199
  handleRemovedVehicule(index: number): void {
1153
- // (2) votre fonction de suppression de l'élément ainsi que la transmission de l'évènement
1200
+ // (2) your function removing the item and sending the event
1154
1201
  this.vehicules.update((vehicules) =>
1155
1202
  vehicules.filter((_vehicule, i) => i !== index),
1156
- ); // suppression de l'élément
1157
- this.getValidValueAndUpdateNgModel(); // Mise à jour de votre modèle
1158
- this.removedVehicule.emit(); // (1) on remonte l'évènement
1203
+ ); // removal of the item
1204
+ this.getValidValueAndUpdateNgModel(); // Update of your model
1205
+ this.removedVehicule.emit(); // (1) the event is sent up
1159
1206
  }
1160
1207
 
1161
1208
  override onModelChange(value: Vehicule[] | null): void {
@@ -1163,19 +1210,19 @@ export class VehiculesListComponent extends FoehnInputComponent<Vehicule[]> {
1163
1210
  }
1164
1211
 
1165
1212
  addVehicule(): void {
1166
- // (3) Gestion de l'ajout d'un élément
1213
+ // (3) Handling of the addition of an item
1167
1214
  // ...code
1168
1215
  }
1169
1216
  }
1170
1217
  ```
1171
1218
 
1172
- > 💡 Plus besoin de définir une `trackByFn` dédiée : la nouvelle syntaxe `@for` utilise directement
1173
- > l'expression `track` (ici `vehicule.id`).
1219
+ > 💡 No need to define a dedicated `trackByFn` anymore: the new `@for` syntax directly uses
1220
+ > the `track` expression (here `vehicule.id`).
1174
1221
 
1175
1222
  **demande-page**
1176
1223
 
1177
- `demande-page.component.html` utilisera votre composant de liste de véhicule (1) et écoutera pour
1178
- l'événement de suppression (2).
1224
+ `demande-page.component.html` uses your vehicle list component (1) and listens to
1225
+ the removal event (2).
1179
1226
 
1180
1227
  ```html
1181
1228
  <foehn-form>
@@ -1189,7 +1236,7 @@ l'événement de suppression (2).
1189
1236
  [required]="true"
1190
1237
  (removedVehicule)="handleRemovedVehicule()"
1191
1238
  />
1192
- <!-- (1) Utilisation de votre liste - (2) Appel de la méthode qui sauvegardera les données dans GESDEM afin de rafraîchir les erreurs -->
1239
+ <!-- (1) Use of your list - (2) Call of the method saving the data in GESDEM to refresh the errors -->
1193
1240
 
1194
1241
  <foehn-navigation
1195
1242
  id="navigation"
@@ -1199,8 +1246,8 @@ l'événement de suppression (2).
1199
1246
  </foehn-form>
1200
1247
  ```
1201
1248
 
1202
- `demande-page.component.ts` se chargera de l'appel à GESDEM afin de re-générer les erreurs du
1203
- formulaire et ainsi rafraîchir l'index.
1249
+ `demande-page.component.ts` calls GESDEM to generate the errors of the
1250
+ form again, and thus refresh the index.
1204
1251
 
1205
1252
  ```ts
1206
1253
  @Component({
@@ -1222,20 +1269,20 @@ export class DemandePageComponent
1222
1269
  }
1223
1270
  ```
1224
1271
 
1225
- ## Validation du formulaire
1272
+ ## Form validation
1226
1273
 
1227
- > ℹ️ Lors de la sauvegarde de votre formulaire dans GESDEM, l'`AbstractPageComponent` gère pour vous
1228
- > l'affichage éventuel des erreurs.
1274
+ > ℹ️ When your form is saved in GESDEM, the `AbstractPageComponent` handles the
1275
+ > display of the errors for you.
1229
1276
  >
1230
- > Si par contre vous souhaitez sauvegarder les données ailleurs que dans GESDEM, vous serez confronté
1231
- > à la gestion manuelle de l'affichage des erreurs.
1277
+ > If, however, you want to save the data somewhere else than in GESDEM, you will have to handle
1278
+ > the display of the errors manually.
1232
1279
 
1233
- ### Gestion standard
1280
+ ### Standard handling
1234
1281
 
1235
- > ℹ️ Lors de l'enregistrement de vos données dans GESDEM, l'affichage des erreurs est automatiquement
1236
- > géré pour vous. Cependant, vous devez impérativement respecter certaines règles.
1282
+ > ℹ️ When your data is saved in GESDEM, the display of the errors is handled automatically
1283
+ > for you. However, you must respect some rules.
1237
1284
 
1238
- Prenons un exemple de modèle simple côté front à deux niveaux de profondeur :
1285
+ Let's take an example of a simple front-end model with two levels of depth:
1239
1286
 
1240
1287
  `identification.ts`
1241
1288
 
@@ -1255,8 +1302,8 @@ export class BusinessForm {
1255
1302
  }
1256
1303
  ```
1257
1304
 
1258
- > ⚠️ Le modèle ci-dessus devra impérativement être reflété dans le `name` de vos composants
1259
- > `foehn-input-*` de votre page.
1305
+ > ⚠️ The model above must be reflected in the `name` of the `foehn-input-*`
1306
+ > components of your page.
1260
1307
 
1261
1308
  `page-1.html`
1262
1309
 
@@ -1265,7 +1312,7 @@ export class BusinessForm {
1265
1312
  [(model)]="form.identification.firstName"
1266
1313
  name="identification.firstName"
1267
1314
  />
1268
- <!-- 2 niveaux de profondeur -->
1315
+ <!-- 2 levels of depth -->
1269
1316
 
1270
1317
  <foehn-input-text
1271
1318
  [(model)]="form.identification.lastName"
@@ -1273,26 +1320,26 @@ export class BusinessForm {
1273
1320
  />
1274
1321
 
1275
1322
  <foehn-boolean-checkbox [(model)]="form.acceptCookies" name="acceptCookies" />
1276
- <!-- 1 niveau de profondeur -->
1323
+ <!-- 1 level of depth -->
1277
1324
  ```
1278
1325
 
1279
- - Côté back-end, vous pouvez gérer vos erreurs grâce aux annotations (`@NotEmpty`, `@NotNull`, ...),
1280
- et **prestations-be** fera le reste pour vous.
1281
- - Si vous souhaitez remonter une erreur spécifique à un champ de votre formulaire, voici la syntaxe :
1326
+ - On the back-end side, you can handle your errors with the annotations (`@NotEmpty`, `@NotNull`, ...),
1327
+ and **prestations-be** does the rest for you.
1328
+ - If you want to return an error specific to a field of your form, here is the syntax:
1282
1329
 
1283
1330
  ```java
1284
- new AbstractSdkController.FormError("identification.lastName", "ErrorCodeHere", "Le nom est obligatoire si vous ne spécifiez pas de prénom")
1331
+ new AbstractSdkController.FormError("identification.lastName", "ErrorCodeHere", "The last name is required if you don't specify a first name")
1285
1332
  ```
1286
1333
 
1287
- > ⚠️ Le nom de l'erreur doit impérativement correspondre au nom de votre composant dans votre page
1288
- > HTML.
1334
+ > ⚠️ The name of the error must match the name of your component in your HTML
1335
+ > page.
1289
1336
 
1290
- ### Gestion manuelle
1337
+ ### Manual handling
1291
1338
 
1292
1339
  #### Back-end
1293
1340
 
1294
- Côté back-end JAVA, le serveur devra retourner un objet contenant les erreurs du formulaire afin
1295
- d'être compatible avec prestations-ng.
1341
+ On the JAVA back-end side, the server must return an object containing the errors of the form to
1342
+ be compatible with prestations-ng.
1296
1343
 
1297
1344
  ```java
1298
1345
  @Getter
@@ -1306,23 +1353,23 @@ public class PostResponse<T> {
1306
1353
 
1307
1354
  @Builder.Default
1308
1355
  private List<AbstractSdkController.FormError> errors = new ArrayList<>();
1309
- // AbstractSdkController.FormError vient de prestations-be qui est la partie backend de prestations-ng
1356
+ // AbstractSdkController.FormError comes from prestations-be, the backend part of prestations-ng
1310
1357
  }
1311
1358
  ```
1312
1359
 
1313
- Ensuite, ajoutez vos erreurs dans votre modèle avant de le retourner au front-end :
1360
+ Then, add your errors to your model before returning it to the front-end:
1314
1361
 
1315
1362
  ```java
1316
1363
  List<AbstractSdkController.FormError> errors = new ArrayList<>();
1317
- errors.add(new AbstractSdkController.FormError("identification.lastName", "ErrorCodeHere", "Le nom est obligatoire si vous ne spécifiez pas de prénom"));
1364
+ errors.add(new AbstractSdkController.FormError("identification.lastName", "ErrorCodeHere", "The last name is required if you don't specify a first name"));
1318
1365
 
1319
1366
  PostResponse.<MyObject>builder().dto(myObject).errors(errors).build();
1320
1367
  ```
1321
1368
 
1322
1369
  #### Front-end
1323
1370
 
1324
- Côté front-end, comme la validation standard, les noms de vos composants `foehn-input-*` de votre
1325
- page HTML et le nom de vos erreurs devront correspondre.
1371
+ On the front-end side, as for the standard validation, the names of the `foehn-input-*` components of your
1372
+ HTML page and the names of your errors must match.
1326
1373
 
1327
1374
  ```html
1328
1375
  <foehn-input-text
@@ -1331,15 +1378,15 @@ page HTML et le nom de vos erreurs devront correspondre.
1331
1378
  />
1332
1379
  ```
1333
1380
 
1334
- > ⚠️ Au retour de la sauvegarde de vos données, vous allez devoir déclencher manuellement la
1335
- > validation.
1381
+ > ⚠️ When your data has been saved, you will have to trigger the validation
1382
+ > manually.
1336
1383
 
1337
- 1. Dans votre `page.ts`, créez une fonction `handleValidationErrors` pour gérer l'affichage des
1338
- erreurs.
1339
- 2. Cette fonction sera appelée au retour du back-end.
1384
+ 1. In your `page.ts`, create a `handleValidationErrors` function to handle the display of the
1385
+ errors.
1386
+ 2. This function is called when the back-end answers.
1340
1387
 
1341
- > 💡 Point **signals** : plus de `@ViewChild('formWithModels')`. Pour utiliser un composant dans le
1342
- > TypeScript, on utilise un **signal** `viewChild(...)`. On accède ensuite à l'instance en l'invoquant :
1388
+ > 💡 **signals** point: no more `@ViewChild('formWithModels')`. To use a component in the
1389
+ > TypeScript, use a `viewChild(...)` **signal**. Then access the instance by calling it:
1343
1390
  > `this.formWithModels()`.
1344
1391
 
1345
1392
  `page.ts`
@@ -1356,41 +1403,41 @@ import {
1356
1403
  // ...
1357
1404
  })
1358
1405
  export class ... extends AbstractPageComponent<YOUR_MODEL> implements OnInit {
1359
- // point (7 des signaux) : un signal viewChild au lieu de @ViewChild
1406
+ // a viewChild signal instead of @ViewChild
1360
1407
  readonly formWithModels = viewChild(FoehnFormComponent);
1361
1408
 
1362
- // vos propres dépendances via inject()
1409
+ // your own dependencies with inject()
1363
1410
  private readonly httpClient = inject(HttpClient);
1364
1411
 
1365
1412
  ngOnInit(): void {
1366
1413
  super.ngOnInit();
1367
- // Activer l'affichage des erreurs
1414
+ // Enable the display of the errors
1368
1415
  this._validationHandlerService.shouldDisplayErrors(true);
1369
1416
  }
1370
1417
 
1371
- // Fonction de sauvegarde
1418
+ // Save function
1372
1419
  save(): void {
1373
- // Activer l'affichage des erreurs
1420
+ // Enable the display of the errors
1374
1421
  this._validationHandlerService.shouldDisplayErrors(true);
1375
- // Appel au back pour faire votre validation. Le callback ne modifie aucun champ de la page :
1376
- // les services de prestations-ng (growl, erreurs) rafraîchissent eux-mêmes l'affichage, aussi
1377
- // sans zone.js. Un état propre à la page modifié ici devrait être un signal.
1422
+ // Call to the back-end to do your validation. The callback modifies no field of the page:
1423
+ // the services of prestations-ng (growl, errors) refresh the display themselves, also
1424
+ // without zone.js. A state of the page modified here should be a signal.
1378
1425
  this.httpClient
1379
- .post<PostResponse>('api_url_ici', votre_objet)
1426
+ .post<PostResponse>('api_url_here', your_object)
1380
1427
  .subscribe((response: PostResponse) => {
1381
1428
  if (!response.errors.length) {
1382
- // On prévient l'utilisateur que tout s'est bien passé
1429
+ // Tell the user that everything went well
1383
1430
  this._growlService.addWithType(
1384
1431
  GrowlType.SUCCESS,
1385
- 'Enregistrement effectué avec succès',
1432
+ 'Saved successfully',
1386
1433
  );
1387
1434
  }
1388
- // Gestion des erreurs au retour du back-end
1435
+ // Handling of the errors returned by the back-end
1389
1436
  this.handleValidationErrors(response.errors); // point (2)
1390
1437
  });
1391
1438
  }
1392
1439
 
1393
- // Affichage des erreurs
1440
+ // Display of the errors
1394
1441
  private handleValidationErrors(errors: ErrorTemplate[]): void {
1395
1442
  // point (1)
1396
1443
  const form = this.formWithModels();
@@ -1408,21 +1455,21 @@ export class ... extends AbstractPageComponent<YOUR_MODEL> implements OnInit {
1408
1455
 
1409
1456
  ```html
1410
1457
  <foehn-form>
1411
- <!-- ... vos composants foehn-input-* -->
1458
+ <!-- ... your foehn-input-* components -->
1412
1459
  </foehn-form>
1413
1460
  ```
1414
1461
 
1415
- ## Validation manuelle d'un composant
1462
+ ## Manual validation of a component
1416
1463
 
1417
- ### Cacher une erreur
1464
+ ### Hiding an error
1418
1465
 
1419
- > ✅ Utiliser la fonction `markAsDirty()` permet de simuler une interaction avec le composant.
1466
+ > ✅ The `markAsDirty()` function simulates an interaction with the component.
1420
1467
  >
1421
- > ℹ️ Permet de cacher l'erreur d'un composant foehn-input en erreur.
1468
+ > ℹ️ Hides the error of a foehn-input component in error.
1422
1469
 
1423
- ### Afficher une erreur
1470
+ ### Displaying an error
1424
1471
 
1425
- > ✅ Utiliser la fonction `markAsPristine()` remet le composant dans son état d'origine comme si
1426
- > aucune interaction n'avait été effectuée.
1472
+ > ✅ The `markAsPristine()` function puts the component back in its original state, as if
1473
+ > no interaction had happened.
1427
1474
  >
1428
- > ℹ️ Permet d'afficher l'erreur d'un composant foehn-input en erreur.
1475
+ > ℹ️ Displays the error of a foehn-input component in error.