@dsivd/prestations-ng 19.2.0-beta.12 → 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);
@@ -469,10 +471,54 @@ ngOnInit(): void {
469
471
  }
470
472
  ```
471
473
 
472
- ### Agir sur le DOM après le rendu
474
+ ### Deriving a state from view queries
473
475
 
474
- > ⚠️ `NgZone` (`onStable`, `onMicrotaskEmpty`, `run()`, ...) ne fonctionne plus sans zone.js, et un
475
- > `setTimeout()` n'attend pas le rendu. Pour agir sur le DOM rendu (focus, scroll, ...), utilisez
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
519
+
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
476
522
  > `afterNextRender()`.
477
523
 
478
524
  ```ts
@@ -483,18 +529,18 @@ private readonly injector = inject(Injector);
483
529
 
484
530
  showDetails(): void {
485
531
  this.detailsVisible.set(true);
486
- // Le champ n'existe qu'une fois le détail rendu
532
+ // The field only exists once the details are rendered
487
533
  afterNextRender(() => this.detailsField()?.nativeElement.focus(), {
488
534
  injector: this.injector,
489
535
  });
490
536
  }
491
537
  ```
492
538
 
493
- ### Tester un composant zoneless
539
+ ### Testing a zoneless component
494
540
 
495
- > ⚠️ `fixture.detectChanges()` **force** le rafraîchissement : un composant qui oublie de prévenir
496
- > Angular (champ au lieu d'un signal, `markForCheck()` manquant, ...) passe le test, mais n'est pas
497
- > 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
498
544
  > `await fixture.whenStable()`.
499
545
 
500
546
  ```ts
@@ -506,22 +552,22 @@ it('should display the details', async () => {
506
552
  });
507
553
  ```
508
554
 
509
- `await fixture.whenStable()` attend aussi les tâches en cours, comme les requêtes HTTP : le test ne
510
- 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`).
511
557
 
512
- Gardez `fixture.detectChanges()` pour :
558
+ Keep `fixture.detectChanges()` for:
513
559
 
514
- - vérifier un état intermédiaire, avant la fin d'un traitement asynchrone
515
- - les tests avec `vi.useFakeTimers()` : le rafraîchissement d'Angular passe par des timers, qu'il
516
- 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
517
563
 
518
- > 💡 Pour que le test passe par le même chemin que l'utilisateur, déclenchez les événements dans le
519
- > DOM (`input.dispatchEvent(new Event('change'))`, `button.click()`, ...) plutôt que d'appeler les
520
- > 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.
521
567
 
522
- ## Le routage de vos pages
568
+ ## Routing your pages
523
569
 
524
- ### Formulaire simple
570
+ ### Simple form
525
571
 
526
572
  `app.routes.ts`
527
573
 
@@ -561,14 +607,14 @@ export const routes: Routes = [
561
607
  ];
562
608
  ```
563
609
 
564
- ### Formulaire complexe (sous-routes)
610
+ ### Complex form (sub-routes)
565
611
 
566
- > ℹ️ Préférez l'utilisation de **sous-routes** (`*.routes.ts`) pour regrouper vos routes et ainsi
567
- > mieux structurer votre projet.
612
+ > ℹ️ Prefer **sub-routes** (`*.routes.ts`) to group your routes and thus better structure your
613
+ > project.
568
614
  >
569
- > Comme il n'y a plus de modules (composants **standalone**), on ne charge plus un `*.module.ts` mais
570
- > un fichier de routes qui exporte une constante `Routes`. Voir [UPGRADING_V19.md](UPGRADING_V19.md)
571
- > 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.
572
618
 
573
619
  `app.routes.ts`
574
620
 
@@ -608,9 +654,9 @@ export const routes: Routes = [
608
654
  ];
609
655
  ```
610
656
 
611
- > 💡 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`):
612
658
  > `loadChildren: () => import('./modules/welcome/welcome.routes').then((m) => m.welcome_routes)`
613
- > au lieu de `import('./modules/welcome/welcome.module').then((m) => m.WelcomeModule)`.
659
+ > instead of `import('./modules/welcome/welcome.module').then((m) => m.WelcomeModule)`.
614
660
 
615
661
  `welcome.routes.ts`
616
662
 
@@ -658,45 +704,44 @@ export const inscription_routes: Routes = [
658
704
  ];
659
705
  ```
660
706
 
661
- > 💡 Vous pouvez aussi charger paresseusement (`lazy-load`) un composant :
707
+ > 💡 You can also lazy-load a component:
662
708
  >
663
709
  > ```ts
664
710
  > { path: 'list', loadComponent: () => import('./demande-list-page/demande-list-page.component').then((m) => m.DemandeListPageComponent) },
665
711
  > ```
666
712
 
667
- ## Création d'un composant réutilisable
713
+ ## Creating a reusable component
668
714
 
669
- > ℹ️ Créer un composant réutilisable en Angular n'est pas difficile mais pour qu'il soit adapté à
670
- > prestations-ng, c'est une autre affaire. Il va falloir respecter certaines règles, sans quoi, ça ne
671
- > 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.
672
717
 
673
- ### Exemple avec foehn-input-date de prestations-ng
718
+ ### Example with foehn-input-date of prestations-ng
674
719
 
675
- Votre composant doit :
720
+ Your component must:
676
721
 
677
- 1. `selector` devrait commencer par `"app-..."` pour éviter d'éventuels conflits de nommage
678
- 2. avoir un `providers`
679
- 3. étendre `FoehnInputComponent` et vous devez lui préciser votre modèle de données
680
- 4. répartir les attributs de votre modèle de données dans des **signaux** publics utilisés dans
681
- votre HTML
682
- 5. implémenter une fonction `update` (ici ce sera `updateDate`) qui fera appel à `updateNgModel` pour
683
- maintenir la synchronisation de votre modèle de données avec votre page
684
- 6. implémenter une fonction `handleUserInput` qui fera appel à `handleChange` pour continuer à
685
- notifier le parent des changements de comportement de votre composant
686
- 7. implémenter une fonction `onModelChange` qui vous permettra d'initialiser correctement les
687
- signaux correspondant à votre modèle
688
- 8. implémenter une fonction `getValidValue` afin de reconstruire votre modèle de données avant de le
689
- retransmettre à votre page et retourner `null` si tous les attributs de votre objet sont vides (ça
690
- 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)
691
736
 
692
- > 🚨 Points **standalone / signals** à respecter :
737
+ > 🚨 **standalone / signals** points to respect:
693
738
  >
694
- > - **Pas de `multi: true`** dans le `providers` (retiré depuis la migration standalone).
695
- > - Les `imports` du composant se déclarent **directement** dans le décorateur `@Component`. Chaque
696
- > composant charge ce dont il a besoin (voir `foehn-input-date` dans ce projet).
697
- > - L'état affiché par le composant est tenu dans des **signaux** (point 4) : `onModelChange` est
698
- > appelé lorsque le modèle change, parfois en dehors d'un événement du template, et un simple champ
699
- > 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.
700
745
 
701
746
  `input-date.component.ts`
702
747
 
@@ -709,7 +754,7 @@ import { FoehnValidationAlertsComponent } from '../foehn-validation-alerts/foehn
709
754
  import { SdkDictionaryPipe } from '../sdk-dictionary/sdk-dictionary.pipe';
710
755
 
711
756
  @Component({
712
- // point (1) : dans votre prestation, le selector sera plutôt 'app-...'
757
+ // point (1): in your prestation, the selector will rather be 'app-...'
713
758
  selector: 'app-input-date',
714
759
  templateUrl: './input-date.component.html',
715
760
  // point (2)
@@ -717,10 +762,10 @@ import { SdkDictionaryPipe } from '../sdk-dictionary/sdk-dictionary.pipe';
717
762
  {
718
763
  provide: FoehnInputComponent,
719
764
  useExisting: forwardRef(() => InputDateComponent),
720
- // ⚠️ plus de `multi: true` en standalone
765
+ // ⚠️ no more `multi: true` in standalone
721
766
  },
722
767
  ],
723
- // 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
724
769
  imports: [
725
770
  FoehnValidationAlertsComponent,
726
771
  FoehnInputNumberComponent,
@@ -728,9 +773,9 @@ import { SdkDictionaryPipe } from '../sdk-dictionary/sdk-dictionary.pipe';
728
773
  ],
729
774
  })
730
775
  export class InputDateComponent extends FoehnInputComponent<number[]> {
731
- // point (3) : extends FoehnInputComponent<number[]>
776
+ // point (3): extends FoehnInputComponent<number[]>
732
777
 
733
- // 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
734
779
  readonly day = signal<string | null>(null);
735
780
  readonly month = signal<string | null>(null);
736
781
  readonly year = signal<string | null>(null);
@@ -789,17 +834,17 @@ export class InputDateComponent extends FoehnInputComponent<number[]> {
789
834
  }
790
835
  ```
791
836
 
792
- Chaque `foehn-input-*` utilisé dans votre vue doit :
837
+ Each `foehn-input-*` used in your view must:
793
838
 
794
- 1. avoir un `id` qui concatène l'appel à la fonction `buildId()` et le nom de l'attribut de votre objet
795
- 2. avoir un `name` qui concatène l'input **signal** `name()` et le nom de l'attribut de votre objet
796
- 3. avoir un `label`
797
- 4. utiliser votre signal public comme model en double-binding (banana in the box) : `[(model)]="day"`
798
- lit et écrit le signal (sans les parenthèses)
799
- 5. faire appel à `updateDate()` lors du déclenchement de l'événement `(modelChange)`
800
- 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
801
846
 
802
- > 💡 `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
803
848
  > `name + '_day'`).
804
849
 
805
850
  `input-date.component.html`
@@ -842,20 +887,20 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
842
887
  />
843
888
  ```
844
889
 
845
- ### Afficher la validation globale du composant
890
+ ### Displaying the global validation of the component
846
891
 
847
- - Il faut ajouter dans la partie HTML de votre composant le code décrit ci-dessous
848
- - Remonter une erreur correspondant au `name` de votre composant lorsque celui-ci est utilisé dans
849
- 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.
850
895
 
851
- > 💡 Points **signals** à noter dans ce template :
896
+ > 💡 **signals** points to note in this template:
852
897
  >
853
- > - `@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
854
899
  > (`label()`, `required()`, `isLabelSrOnly()`, `hideNotRequiredExtraLabel()`, `helpText()`).
855
- > - `[ngClass]` est remplacé par des bindings `[class.xxx]="..."`.
900
+ > - `[ngClass]` is replaced by `[class.xxx]="..."` bindings.
856
901
 
857
902
  ```html
858
- <!-- (obligatoire) Affichera une barre verticale rouge sur l'entier du composant -->
903
+ <!-- (mandatory) Displays a red vertical bar on the whole component -->
859
904
  <div
860
905
  class="form-group"
861
906
  [class.has-danger]="hasErrorsToDisplay()"
@@ -863,12 +908,12 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
863
908
  [attr.id]="buildId('Container')"
864
909
  tabindex="-1"
865
910
  >
866
- <!-- (obligatoire) Permet de regrouper vos champs pour une validation globale -->
911
+ <!-- (mandatory) Groups your fields for a global validation -->
867
912
  <fieldset
868
913
  [attr.aria-describedby]="getDescribedBy()"
869
914
  [attr.aria-invalid]="hasErrorsToDisplay() || null"
870
915
  >
871
- <!-- (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 -->
872
917
  @if (!!label()) {
873
918
  <legend
874
919
  [class.visually-hidden]="isLabelSrOnly()"
@@ -883,10 +928,10 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
883
928
  </legend>
884
929
  }
885
930
 
886
- <!-- (obligatoire) -->
931
+ <!-- (mandatory) -->
887
932
  <foehn-validation-alerts [component]="this" />
888
933
 
889
- <!-- (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 -->
890
935
  @if (helpText()) {
891
936
  <small
892
937
  [attr.id]="buildId() + 'Help'"
@@ -895,36 +940,36 @@ Chaque `foehn-input-*` utilisé dans votre vue doit :
895
940
  ></small>
896
941
  }
897
942
 
898
- <!-- ...vos foehn-input-* ici -->
943
+ <!-- ...your foehn-input-* here -->
899
944
  </fieldset>
900
945
  </div>
901
946
  ```
902
947
 
903
- ### Éviter des problèmes d'affichage d'erreurs dans une boucle `@for`
948
+ ### Avoiding error display problems in a `@for` loop
904
949
 
905
- > ⚠️ Ne pas oublier de mettre un `track` avec un identifiant unique dans la boucle `@for` sinon la
906
- > validation ne s'affichera pas. Quand un identifiant unique tel que `id` n'est pas possible, tracker
907
- > 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`.
908
953
 
909
- > ℹ️ Dans une boucle `@for` d'Angular avec un composant `FoehnInputComponent`, si vous validez votre
910
- > formulaire, les erreurs remontées sont synchronisées avec l'index de vos composants répétés dans la
911
- > 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.
912
957
  >
913
- > **Scénario :** Dans votre liste, lorsque vous supprimez un composant qui contient des erreurs à
914
- > l'index 0 (par ex.) et que le composant suivant (index 1) n'a pas d'erreur ou pas les mêmes
915
- > erreurs, alors ce dernier se retrouve avec les erreurs du composant fraîchement supprimé à
916
- > 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.
917
962
  >
918
- > **Explication :** Ce phénomène est dû au fait que les erreurs remontées par GESDEM ne se
919
- > rafraîchissent que lors de la demande de sauvegarde des données. Du coup, le composant supprimé à
920
- > l'index 0 est remplacé par celui à l'index 1 mais les erreurs à l'index 0, elles, existent toujours
921
- > 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.
922
967
 
923
- Voici la bonne pratique à mettre en place pour palier à ces problèmes d'affichage d'erreurs dans une
924
- boucle `@for`.
968
+ Here is the good practice to put in place to work around these error display problems in a
969
+ `@for` loop.
925
970
 
926
- Lorsque vous supprimez un élément dans votre liste, vous devez appeler une méthode se trouvant dans
927
- 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:
928
973
 
929
974
  ```ts
930
975
  /*
@@ -934,30 +979,30 @@ votre `AbstractPageComponent`. Cette méthode contiendra le code suivant :
934
979
  this._gesdemService.validate(this.form).pipe(first()).subscribe();
935
980
  ```
936
981
 
937
- 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):
938
983
 
939
- > 💡 Plus de `constructor` : on fait de l'**inject**. Les dépendances de `AbstractPageComponent`
940
- > (dont `_dictionaryService` et `_gesdemService`) sont déjà injectées dans la classe abstraite. Pour
941
- > 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()`.
942
987
 
943
988
  ```ts
944
989
  @Component({
945
- templateUrl: './votre-page.component.html',
990
+ templateUrl: './your-page.component.html',
946
991
  imports: [
947
- // ... vos composants foehn-*
992
+ // ... your foehn-* components
948
993
  ],
949
994
  })
950
- export class VotrePageComponent
995
+ export class YourPageComponent
951
996
  extends AbstractPageComponent<BusinessForm>
952
997
  implements OnInit
953
998
  {
954
- // vos propres dépendances : inject() au lieu du constructor
999
+ // your own dependencies: inject() instead of the constructor
955
1000
  private readonly metaService = inject(MetaService);
956
1001
 
957
1002
  ngOnInit(): void {
958
1003
  super.ngOnInit();
959
1004
  this.setPageTitle(
960
- this._dictionaryService.getKeySync('votre-page.title'),
1005
+ this._dictionaryService.getKeySync('your-page.title'),
961
1006
  );
962
1007
  }
963
1008
 
@@ -976,12 +1021,12 @@ export class VotrePageComponent
976
1021
  }
977
1022
  ```
978
1023
 
979
- Il ne tient qu'à vous de décider comment appeler cette méthode lors de la suppression de l'élément de
980
- votre liste.
1024
+ It's up to you to decide how to call this method when the item is removed from
1025
+ your list.
981
1026
 
982
- 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:
983
1028
 
984
- - Structure des dossiers et fichiers
1029
+ - Structure of the folders and files
985
1030
 
986
1031
  ```text
987
1032
  demande-page/
@@ -998,7 +1043,7 @@ demande-page/
998
1043
 
999
1044
  **vehicule-item**
1000
1045
 
1001
- `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).
1002
1047
 
1003
1048
  ```html
1004
1049
  <div
@@ -1011,7 +1056,7 @@ demande-page/
1011
1056
  <foehn-validation-alerts [component]="this" />
1012
1057
 
1013
1058
  <div class="row d-flex align-items-end vehicule-infos">
1014
- <!-- ... vos composants foehn-input-* -->
1059
+ <!-- ... your foehn-input-* components -->
1015
1060
 
1016
1061
  <div class="col-md-2 mt-md-0 mt-xs-2">
1017
1062
  <button class="btn btn-danger" (click)="removeVehicule()">
@@ -1023,10 +1068,10 @@ demande-page/
1023
1068
  </div>
1024
1069
  ```
1025
1070
 
1026
- `vehicule-item.component.ts` contient la fonction de suppression qui va remonter l'événement à son
1027
- 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).
1028
1073
 
1029
- > 💡 `@Output() + EventEmitter` sont remplacés par la fonction `output()`.
1074
+ > 💡 `@Output() + EventEmitter` are replaced by the `output()` function.
1030
1075
 
1031
1076
  ```ts
1032
1077
  @Component({
@@ -1040,7 +1085,7 @@ parent (la liste des éléments).
1040
1085
  ],
1041
1086
  imports: [
1042
1087
  FoehnValidationAlertsComponent,
1043
- // ... vos foehn-input-*
1088
+ // ... your foehn-input-*
1044
1089
  SdkDictionaryPipe,
1045
1090
  ],
1046
1091
  })
@@ -1057,11 +1102,11 @@ export class VehiculeItemComponent extends FoehnInputComponent<Vehicule> {
1057
1102
 
1058
1103
  **vehicules-list**
1059
1104
 
1060
- `vehicules-list.component.html` contient votre boucle `@for` (1) et votre composant répétable (2) qui
1061
- 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.
1062
1107
 
1063
- > ⚠️ Dans la boucle `@for` ne pas oublier de mettre un `track` (identifiant unique) sinon la
1064
- > 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`.
1065
1110
 
1066
1111
  ```html
1067
1112
  <div
@@ -1094,9 +1139,9 @@ devra lui aussi remonter l'événement.
1094
1139
  ></small>
1095
1140
  }
1096
1141
 
1097
- <!-- (1) votre boucle @for + track (identifiant unique) -->
1142
+ <!-- (1) your @for loop + track (unique identifier) -->
1098
1143
  @for (vehicule of vehicules(); track vehicule.id; let i = $index) {
1099
- <!-- (2) votre composant réutilisable -->
1144
+ <!-- (2) your reusable component -->
1100
1145
  <app-vehicule-item
1101
1146
  [name]="name() + '[' + i + ']'"
1102
1147
  [model]="vehicule"
@@ -1112,15 +1157,15 @@ devra lui aussi remonter l'événement.
1112
1157
  </div>
1113
1158
  ```
1114
1159
 
1115
- > 💡 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
1116
1161
  > `[name]="name + '[' + i + ']'"`).
1117
1162
  >
1118
- > 💡 La variable d'une boucle `@for` ne peut pas être modifiée : on passe l'élément avec `[model]` et
1119
- > 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)`).
1120
1165
 
1121
- `vehicules-list.component.ts` remonte à son tour l'événement (1) au parent (`AbstractPageComponent`)
1122
- ainsi que supprimer l'élément de la liste (2). C'est aussi ici que vous pouvez gérer l'ajout d'un
1123
- é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).
1124
1169
 
1125
1170
  ```ts
1126
1171
  @Component({
@@ -1139,25 +1184,25 @@ ainsi que supprimer l'élément de la liste (2). C'est aussi ici que vous pouvez
1139
1184
  ],
1140
1185
  })
1141
1186
  export class VehiculesListComponent extends FoehnInputComponent<Vehicule[]> {
1142
- readonly removedVehicule = output<void>(); // (1) événement à remonter au parent
1187
+ readonly removedVehicule = output<void>(); // (1) event to send up to the parent
1143
1188
 
1144
- // 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
1145
1190
  readonly vehicules = signal<Vehicule[]>([]);
1146
1191
 
1147
1192
  updateVehiculesList(vehicule: Vehicule, index: number): void {
1148
1193
  this.vehicules.update((vehicules) =>
1149
1194
  vehicules.map((item, i) => (i === index ? vehicule : item)),
1150
1195
  );
1151
- this.getValidValueAndUpdateNgModel(); // Mise à jour de votre modèle
1196
+ this.getValidValueAndUpdateNgModel(); // Update of your model
1152
1197
  }
1153
1198
 
1154
1199
  handleRemovedVehicule(index: number): void {
1155
- // (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
1156
1201
  this.vehicules.update((vehicules) =>
1157
1202
  vehicules.filter((_vehicule, i) => i !== index),
1158
- ); // suppression de l'élément
1159
- this.getValidValueAndUpdateNgModel(); // Mise à jour de votre modèle
1160
- 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
1161
1206
  }
1162
1207
 
1163
1208
  override onModelChange(value: Vehicule[] | null): void {
@@ -1165,19 +1210,19 @@ export class VehiculesListComponent extends FoehnInputComponent<Vehicule[]> {
1165
1210
  }
1166
1211
 
1167
1212
  addVehicule(): void {
1168
- // (3) Gestion de l'ajout d'un élément
1213
+ // (3) Handling of the addition of an item
1169
1214
  // ...code
1170
1215
  }
1171
1216
  }
1172
1217
  ```
1173
1218
 
1174
- > 💡 Plus besoin de définir une `trackByFn` dédiée : la nouvelle syntaxe `@for` utilise directement
1175
- > 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`).
1176
1221
 
1177
1222
  **demande-page**
1178
1223
 
1179
- `demande-page.component.html` utilisera votre composant de liste de véhicule (1) et écoutera pour
1180
- 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).
1181
1226
 
1182
1227
  ```html
1183
1228
  <foehn-form>
@@ -1191,7 +1236,7 @@ l'événement de suppression (2).
1191
1236
  [required]="true"
1192
1237
  (removedVehicule)="handleRemovedVehicule()"
1193
1238
  />
1194
- <!-- (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 -->
1195
1240
 
1196
1241
  <foehn-navigation
1197
1242
  id="navigation"
@@ -1201,8 +1246,8 @@ l'événement de suppression (2).
1201
1246
  </foehn-form>
1202
1247
  ```
1203
1248
 
1204
- `demande-page.component.ts` se chargera de l'appel à GESDEM afin de re-générer les erreurs du
1205
- 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.
1206
1251
 
1207
1252
  ```ts
1208
1253
  @Component({
@@ -1224,20 +1269,20 @@ export class DemandePageComponent
1224
1269
  }
1225
1270
  ```
1226
1271
 
1227
- ## Validation du formulaire
1272
+ ## Form validation
1228
1273
 
1229
- > ℹ️ Lors de la sauvegarde de votre formulaire dans GESDEM, l'`AbstractPageComponent` gère pour vous
1230
- > l'affichage éventuel des erreurs.
1274
+ > ℹ️ When your form is saved in GESDEM, the `AbstractPageComponent` handles the
1275
+ > display of the errors for you.
1231
1276
  >
1232
- > Si par contre vous souhaitez sauvegarder les données ailleurs que dans GESDEM, vous serez confronté
1233
- > à 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.
1234
1279
 
1235
- ### Gestion standard
1280
+ ### Standard handling
1236
1281
 
1237
- > ℹ️ Lors de l'enregistrement de vos données dans GESDEM, l'affichage des erreurs est automatiquement
1238
- > 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.
1239
1284
 
1240
- 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:
1241
1286
 
1242
1287
  `identification.ts`
1243
1288
 
@@ -1257,8 +1302,8 @@ export class BusinessForm {
1257
1302
  }
1258
1303
  ```
1259
1304
 
1260
- > ⚠️ Le modèle ci-dessus devra impérativement être reflété dans le `name` de vos composants
1261
- > `foehn-input-*` de votre page.
1305
+ > ⚠️ The model above must be reflected in the `name` of the `foehn-input-*`
1306
+ > components of your page.
1262
1307
 
1263
1308
  `page-1.html`
1264
1309
 
@@ -1267,7 +1312,7 @@ export class BusinessForm {
1267
1312
  [(model)]="form.identification.firstName"
1268
1313
  name="identification.firstName"
1269
1314
  />
1270
- <!-- 2 niveaux de profondeur -->
1315
+ <!-- 2 levels of depth -->
1271
1316
 
1272
1317
  <foehn-input-text
1273
1318
  [(model)]="form.identification.lastName"
@@ -1275,26 +1320,26 @@ export class BusinessForm {
1275
1320
  />
1276
1321
 
1277
1322
  <foehn-boolean-checkbox [(model)]="form.acceptCookies" name="acceptCookies" />
1278
- <!-- 1 niveau de profondeur -->
1323
+ <!-- 1 level of depth -->
1279
1324
  ```
1280
1325
 
1281
- - Côté back-end, vous pouvez gérer vos erreurs grâce aux annotations (`@NotEmpty`, `@NotNull`, ...),
1282
- et **prestations-be** fera le reste pour vous.
1283
- - 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:
1284
1329
 
1285
1330
  ```java
1286
- 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")
1287
1332
  ```
1288
1333
 
1289
- > ⚠️ Le nom de l'erreur doit impérativement correspondre au nom de votre composant dans votre page
1290
- > HTML.
1334
+ > ⚠️ The name of the error must match the name of your component in your HTML
1335
+ > page.
1291
1336
 
1292
- ### Gestion manuelle
1337
+ ### Manual handling
1293
1338
 
1294
1339
  #### Back-end
1295
1340
 
1296
- Côté back-end JAVA, le serveur devra retourner un objet contenant les erreurs du formulaire afin
1297
- 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.
1298
1343
 
1299
1344
  ```java
1300
1345
  @Getter
@@ -1308,23 +1353,23 @@ public class PostResponse<T> {
1308
1353
 
1309
1354
  @Builder.Default
1310
1355
  private List<AbstractSdkController.FormError> errors = new ArrayList<>();
1311
- // 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
1312
1357
  }
1313
1358
  ```
1314
1359
 
1315
- 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:
1316
1361
 
1317
1362
  ```java
1318
1363
  List<AbstractSdkController.FormError> errors = new ArrayList<>();
1319
- 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"));
1320
1365
 
1321
1366
  PostResponse.<MyObject>builder().dto(myObject).errors(errors).build();
1322
1367
  ```
1323
1368
 
1324
1369
  #### Front-end
1325
1370
 
1326
- Côté front-end, comme la validation standard, les noms de vos composants `foehn-input-*` de votre
1327
- 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.
1328
1373
 
1329
1374
  ```html
1330
1375
  <foehn-input-text
@@ -1333,15 +1378,15 @@ page HTML et le nom de vos erreurs devront correspondre.
1333
1378
  />
1334
1379
  ```
1335
1380
 
1336
- > ⚠️ Au retour de la sauvegarde de vos données, vous allez devoir déclencher manuellement la
1337
- > validation.
1381
+ > ⚠️ When your data has been saved, you will have to trigger the validation
1382
+ > manually.
1338
1383
 
1339
- 1. Dans votre `page.ts`, créez une fonction `handleValidationErrors` pour gérer l'affichage des
1340
- erreurs.
1341
- 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.
1342
1387
 
1343
- > 💡 Point **signals** : plus de `@ViewChild('formWithModels')`. Pour utiliser un composant dans le
1344
- > 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:
1345
1390
  > `this.formWithModels()`.
1346
1391
 
1347
1392
  `page.ts`
@@ -1358,41 +1403,41 @@ import {
1358
1403
  // ...
1359
1404
  })
1360
1405
  export class ... extends AbstractPageComponent<YOUR_MODEL> implements OnInit {
1361
- // point (7 des signaux) : un signal viewChild au lieu de @ViewChild
1406
+ // a viewChild signal instead of @ViewChild
1362
1407
  readonly formWithModels = viewChild(FoehnFormComponent);
1363
1408
 
1364
- // vos propres dépendances via inject()
1409
+ // your own dependencies with inject()
1365
1410
  private readonly httpClient = inject(HttpClient);
1366
1411
 
1367
1412
  ngOnInit(): void {
1368
1413
  super.ngOnInit();
1369
- // Activer l'affichage des erreurs
1414
+ // Enable the display of the errors
1370
1415
  this._validationHandlerService.shouldDisplayErrors(true);
1371
1416
  }
1372
1417
 
1373
- // Fonction de sauvegarde
1418
+ // Save function
1374
1419
  save(): void {
1375
- // Activer l'affichage des erreurs
1420
+ // Enable the display of the errors
1376
1421
  this._validationHandlerService.shouldDisplayErrors(true);
1377
- // Appel au back pour faire votre validation. Le callback ne modifie aucun champ de la page :
1378
- // les services de prestations-ng (growl, erreurs) rafraîchissent eux-mêmes l'affichage, aussi
1379
- // 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.
1380
1425
  this.httpClient
1381
- .post<PostResponse>('api_url_ici', votre_objet)
1426
+ .post<PostResponse>('api_url_here', your_object)
1382
1427
  .subscribe((response: PostResponse) => {
1383
1428
  if (!response.errors.length) {
1384
- // On prévient l'utilisateur que tout s'est bien passé
1429
+ // Tell the user that everything went well
1385
1430
  this._growlService.addWithType(
1386
1431
  GrowlType.SUCCESS,
1387
- 'Enregistrement effectué avec succès',
1432
+ 'Saved successfully',
1388
1433
  );
1389
1434
  }
1390
- // Gestion des erreurs au retour du back-end
1435
+ // Handling of the errors returned by the back-end
1391
1436
  this.handleValidationErrors(response.errors); // point (2)
1392
1437
  });
1393
1438
  }
1394
1439
 
1395
- // Affichage des erreurs
1440
+ // Display of the errors
1396
1441
  private handleValidationErrors(errors: ErrorTemplate[]): void {
1397
1442
  // point (1)
1398
1443
  const form = this.formWithModels();
@@ -1410,21 +1455,21 @@ export class ... extends AbstractPageComponent<YOUR_MODEL> implements OnInit {
1410
1455
 
1411
1456
  ```html
1412
1457
  <foehn-form>
1413
- <!-- ... vos composants foehn-input-* -->
1458
+ <!-- ... your foehn-input-* components -->
1414
1459
  </foehn-form>
1415
1460
  ```
1416
1461
 
1417
- ## Validation manuelle d'un composant
1462
+ ## Manual validation of a component
1418
1463
 
1419
- ### Cacher une erreur
1464
+ ### Hiding an error
1420
1465
 
1421
- > ✅ Utiliser la fonction `markAsDirty()` permet de simuler une interaction avec le composant.
1466
+ > ✅ The `markAsDirty()` function simulates an interaction with the component.
1422
1467
  >
1423
- > ℹ️ Permet de cacher l'erreur d'un composant foehn-input en erreur.
1468
+ > ℹ️ Hides the error of a foehn-input component in error.
1424
1469
 
1425
- ### Afficher une erreur
1470
+ ### Displaying an error
1426
1471
 
1427
- > ✅ Utiliser la fonction `markAsPristine()` remet le composant dans son état d'origine comme si
1428
- > 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.
1429
1474
  >
1430
- > ℹ️ Permet d'afficher l'erreur d'un composant foehn-input en erreur.
1475
+ > ℹ️ Displays the error of a foehn-input component in error.