@@ -2,85 +2,92 @@ include ../_util-fns
2
2
3
3
a#top
4
4
:marked
5
- We can improve overall data quality by validating user input for accuracy and completeness.
5
+ Improve overall data quality by validating user input for accuracy and completeness.
6
6
7
- In this cookbook we show how to validate user input in the UI and display useful validation messages
7
+ This cookbook shows how to validate user input in the UI and display useful validation messages
8
8
using first the template-driven forms and then the reactive forms approach.
9
9
.l-sub-section
10
10
:marked
11
- Learn more about these choices in the [Forms chapter.](../guide/forms.html)
11
+ Read more about these choices in the [Forms](../guide/forms.html)
12
+ and the [Reactive Forms](../guide/reactive-forms.html) guides.
12
13
13
14
a#toc
14
15
:marked
15
- ## Table of Contents
16
-
17
- [Simple Template-Driven Forms](#template1)
18
-
19
- [Template-Driven Forms with validation messages in code](#template2)
20
-
21
- [Reactive Forms with validation in code](#reactive)
22
-
23
- [Custom validation](#custom-validation)
24
-
25
- [Testing](#testing)
16
+ ## Contents
17
+
18
+ * [Simple template-driven forms](#template1)
19
+ * [Template-driven forms with validation messages in code](#template2)
20
+ - [Component Class](#component-class)
21
+ - [The benefits of messages in code](#improvement)
22
+ - [`FormModule` and template-driven forms](#formmodule)
23
+ * [Reactive forms with validation in code](#reactive)
24
+ - [Switch to the `ReactiveFormsModule`](#reactive-forms-module)
25
+ - [Component template](#reactive-component-template)
26
+ - [Component class](#reactive-component-class)
27
+ - [`FormBuilder` declaration](#formbuilder)
28
+ - [Committing hero value changes](#committing-changes)
29
+ * [Custom validation](#custom-validation)
30
+ - [Custom validation directive](#custom-validation-directive)
31
+ * [Testing considerations](#testing)
26
32
27
33
a#live-example
28
34
:marked
29
- **Try the live example to see and download the full cookbook source code**
35
+ **Try the live example to see and download the full cookbook source code. **
30
36
live-example( name ="cb-form-validation" embedded img ="cookbooks/form-validation/plunker.png" )
31
37
32
38
.l-main-section
33
39
a#template1
34
40
:marked
35
- ## Simple Template-Driven Forms
41
+ ## Simple template-driven forms
36
42
37
43
In the template-driven approach, you arrange
38
44
[form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
39
45
40
46
You add Angular form directives (mostly directives beginning `ng...`) to help
41
47
Angular construct a corresponding internal control model that implements form functionality.
42
- We say that the control model is _implicit_ in the template.
48
+ In template-drive forms, the control model is _implicit_ in the template.
43
49
44
50
To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
45
51
to the elements. Angular interprets those as well, adding validator functions to the control model.
46
52
47
53
Angular exposes information about the state of the controls including
48
54
whether the user has "touched" the control or made changes and if the control values are valid.
49
55
50
- In the first template validation example,
51
- we add more HTML to read that control state and update the display appropriately.
52
- Here's an excerpt from the template html for a single input box control bound to the hero name:
56
+ In this first template validation example,
57
+ notice the HTML that reads the control state and updates the display appropriately.
58
+ Here's an excerpt from the template HTML for a single input control bound to the hero name:
53
59
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html' ,'name-with-error-msg' ,'template/hero-form-template1.component.html (Hero name)' )( format ='.' )
54
60
55
61
:marked
56
62
Note the following:
57
63
- The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
58
64
59
- - We set the `name` attribute of the input box to `"name"` so Angular can track this input element and associate it
65
+ - The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
60
66
with an Angular form control called `name` in its internal control model.
61
67
62
- - We use the `[(ngModel)]` directive to two-way data bind the input box to the `hero.name` property.
68
+ - The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
63
69
64
- - We set a template variable (`#name`) to the value `"ngModel"` (always `ngModel`).
65
- This gives us a reference to the Angular `NgModel` directive
66
- associated with this control that we can use _in the template_
70
+ - The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
71
+ This gives you a reference to the Angular `NgModel` directive
72
+ associated with this control that you can use _in the template_
67
73
to check for control states such as `valid` and `dirty`.
68
74
69
- - The `*ngIf` on `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
75
+ - The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
70
76
the control is either `dirty` or `touched`.
71
77
72
78
- Each nested `<div>` can present a custom message for one of the possible validation errors.
73
- We've prepared messages for `required`, `minlength`, and `maxlength`.
79
+ There are messages for `required`, `minlength`, and `maxlength`.
74
80
75
81
The full template repeats this kind of layout for each data entry control on the form.
82
+ a#why-check
76
83
.l-sub-section
77
84
:marked
78
85
#### Why check _dirty_ and _touched_?
79
86
80
- We shouldn't show errors for a new hero before the user has had a chance to edit the value.
87
+ The app shouldn't show errors for a new hero before the user has had a chance to edit the value.
81
88
The checks for `dirty` and `touched` prevent premature display of errors.
82
89
83
- Learn about `dirty` and `touched` in the [Forms](../guide/forms.html) chapter .
90
+ Learn about `dirty` and `touched` in the [Forms](../guide/forms.html) guide .
84
91
:marked
85
92
The component class manages the hero model used in the data binding
86
93
as well as other code to support the view.
@@ -102,23 +109,24 @@ a#template1
102
109
.l-main-section
103
110
a#template2
104
111
:marked
105
- ## Template-Driven Forms with validation messages in code
112
+ ## Template-driven forms with validation messages in code
106
113
107
114
While the layout is straightforward,
108
- there are obvious shortcomings with the way we handle validation messages:
115
+ there are obvious shortcomings with the way it's handling validation messages:
109
116
110
117
* It takes a lot of HTML to represent all possible error conditions.
111
118
This gets out of hand when there are many controls and many validation rules.
112
119
113
- * We're not fond of so much JavaScript logic in HTML.
120
+ * There's a lot of JavaScript logic in the HTML.
114
121
115
122
* The messages are static strings, hard-coded into the template.
116
- We often require dynamic messages that we should shape in code .
123
+ It's easier to maintain _dynamic_ messages in the component class .
117
124
118
- We can move the logic and the messages into the component with a few changes to
125
+ In this example, you can move the logic and the messages into the component with a few changes to
119
126
the template and component.
120
127
121
- Here's the hero name again, excerpted from the revised template ("Template 2"), next to the original version:
128
+ Here's the hero name again, excerpted from the revised template
129
+ (Template 2), next to the original version:
122
130
+ makeTabs(
123
131
` cb-form-validation/ts/src/app/template/hero-form-template2.component.html,
124
132
cb-form-validation/ts/src/app/template/hero-form-template1.component.html` ,
@@ -131,26 +139,31 @@ a#template2
131
139
- The hard-code error message `<divs>` are gone.
132
140
133
141
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
134
- It invalidates the control if the user enters "bob" anywhere in the name ([try it](#live-example)).
135
- We discuss [custom validation directives](#custom-validation) later in this cookbook.
142
+ It invalidates the control if the user enters "bob" in the name `<input>`([try it](#live-example)).
143
+ See the [custom validation](#custom-validation) section later in this cookbook for more information
144
+ on custom validation directives.
136
145
137
- - The `#name` template variable is gone because we no longer refer to the Angular control for this element.
146
+ - The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
138
147
139
- - Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
148
+ - Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
140
149
141
- #### Component class
142
- The original component code stays the same.
143
- We _added_ new code to acquire the Angular form control and compose error messages.
150
+ a#component-class
151
+ :marked
152
+ ### Component class
153
+ The original component code for Template 1 stayed the same; however,
154
+ Template 2 requires some changes in the component. This section covers the code
155
+ necessary in Template 2's component class to acquire the Angular
156
+ form control and compose error messages.
144
157
145
158
The first step is to acquire the form control that Angular created from the template by querying for it.
146
159
147
- Look back at the top of the component template where we set the
160
+ Look back at the top of the component template at the
148
161
`#heroForm` template variable in the `<form>` element:
149
162
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html' ,'form-tag' ,'template/hero-form-template1.component.html (form tag)' )( format ='.' )
150
163
151
164
:marked
152
165
The `heroForm` variable is a reference to the control model that Angular derived from the template.
153
- We tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
166
+ Tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
154
167
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' ,'view-child' ,'template/hero-form-template2.component.ts (heroForm)' )( format ='.' )
155
168
156
169
:marked
@@ -159,15 +172,15 @@ a#template2
159
172
- Angular `@ViewChild` queries for a template variable when you pass it
160
173
the name of that variable as a string (`'heroForm'` in this case).
161
174
162
- - The `heroForm` object changes several times during the life of the component, most notably when we add a new hero.
163
- We'll have to re-inspect it periodically .
175
+ - The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
176
+ Periodically inspecting it reveals these changes .
164
177
165
178
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
166
179
when anything changes in the view.
167
180
That's the right time to see if there's a new `heroForm` object.
168
181
169
- - When there _is_ a new `heroForm` model, we subscribe to its `valueChanged ` _Observable_ property.
170
- The `onValueChanged` handler looks for validation errors after every user keystroke.
182
+ - When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges ` _Observable_ property.
183
+ The `onValueChanged` handler looks for validation errors after every keystroke.
171
184
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' ,'handler' ,'template/hero-form-template2.component.ts (handler)' )( format ='.' )
172
185
173
186
:marked
@@ -179,25 +192,30 @@ a#template2
179
192
Only two hero properties have validation rules, `name` and `power`.
180
193
The messages are empty strings when the hero data are valid.
181
194
182
- For each field, the handler
183
- - clears the prior error message if any
184
- - acquires the field's corresponding Angular form control
185
- - if such a control exists _and_ its been changed ("dirty") _and_ its invalid ...
186
- - the handler composes a consolidated error message for all of the control's errors.
195
+ For each field, the `onValueChanged` handler does the following:
196
+ - Clears the prior error message, if any.
197
+ - Acquires the field's corresponding Angular form control.
198
+ - If such a control exists _and_ it's been changed ("dirty")
199
+ _and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
187
200
188
- We'll need some error messages of course, a set for each validated property, one message per validation rule:
201
+ Next, the component needs some error messages of course—a set for each validated property with
202
+ one message per validation rule:
189
203
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts' ,'messages' ,'template/hero-form-template2.component.ts (messages)' )( format ='.' )
190
204
:marked
191
205
Now every time the user makes a change, the `onValueChanged` handler checks for validation errors and produces messages accordingly.
192
-
193
- ### Is this an improvement?
206
+
207
+ a#improvement
208
+ :marked
209
+ ### The benefits of messages in code
194
210
195
211
Clearly the template got substantially smaller while the component code got substantially larger.
196
212
It's not easy to see the benefit when there are just three fields and only two of them have validation rules.
197
213
198
- Consider what happens as we increase the number of validated fields and rules.
214
+ Consider what happens as the number of validated
215
+ fields and rules increases.
199
216
In general, HTML is harder to read and maintain than code.
200
- The initial template was already large and threatening to get rapidly worse as we add more validation message `<divs>`.
217
+ The initial template was already large and threatening to get rapidly worse
218
+ with the addition of more validation message `<div>` elements.
201
219
202
220
After moving the validation messaging to the component,
203
221
the template grows more slowly and proportionally.
@@ -207,31 +225,34 @@ a#template2
207
225
208
226
Both trends are manageable.
209
227
210
- Now that the messages are in code, we have more flexibility. We can compose messages more intelligently .
211
- We can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
228
+ Now that the messages are in code, you have more flexibility and can compose messages more efficiently .
229
+ You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
212
230
In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
213
231
232
+ a#formmodule
233
+ :marked
214
234
### _FormModule_ and template-driven forms
215
235
216
- Angular has two different forms modules — `FormsModule` and `ReactiveFormsModule` —
217
- that correspond with the two approaches to form development.
218
- Both modules come from the same `@angular/forms` library package.
236
+ Angular has two different forms modules—`FormsModule` and
237
+ `ReactiveFormsModule`—that correspond with the
238
+ two approaches to form development. Both modules come
239
+ from the same `@angular/forms` library package.
219
240
220
- We 've been reviewing the "Template-driven" approach which requires the `FormsModule`
221
- Here's how we imported it in the `HeroFormTemplateModule`.
241
+ You 've been reviewing the "Template-driven" approach which requires the `FormsModule`.
242
+ Here's how you imported it in the `HeroFormTemplateModule`.
222
243
223
244
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template.module.ts' ,'' ,'template/hero-form-template.module.ts' )( format ='.' )
224
245
.l-sub-section
225
246
:marked
226
- We haven 't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
247
+ This guide hasn 't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
227
248
form template in this cookbook.
228
249
229
250
They're not germane to the validation story. Look at the [live example](#live-example) if you're interested.
230
251
231
252
.l-main-section
232
253
a#reactive
233
254
:marked
234
- ## Reactive Forms
255
+ ## Reactive forms with validation in code
235
256
236
257
In the template-driven approach, you markup the template with form elements, validation attributes,
237
258
and `ng...` directives from the Angular `FormsModule`.
@@ -244,30 +265,34 @@ a#reactive
244
265
245
266
This approach requires a bit more effort. *You have to write the control model and manage it*.
246
267
247
- In return, you can
248
- * add , change, and remove validation functions on the fly
249
- * manipulate the control model dynamically from within the component
250
- * [test ](#testing) validation and control logic with isolated unit tests.
268
+ This allows you to do the following:
269
+ * Add , change, and remove validation functions on the fly.
270
+ * Manipulate the control model dynamically from within the component.
271
+ * [Test ](#testing) validation and control logic with isolated unit tests.
251
272
252
- The third cookbook sample re-writes the hero form in _reactive forms_ style.
273
+ The following cookbook sample re-writes the hero form in _reactive forms_ style.
253
274
275
+ a#reactive-forms-module
276
+ :marked
254
277
### Switch to the _ReactiveFormsModule_
255
278
The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
256
- The application module for the "Reactive Forms" feature in this sample looks like this:
279
+ The application module for the reactive forms feature in this sample looks like this:
257
280
+ makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts' ,'' ,'src/app/reactive/hero-form-reactive.module.ts' )( format ='.' )
258
281
:marked
259
- The "Reactive Forms" feature module and component are in the `src/app/reactive` folder.
260
- Let's focus on the `HeroFormReactiveComponent` there, starting with its template.
282
+ The reactive forms feature module and component are in the `src/app/reactive` folder.
283
+ Focus on the `HeroFormReactiveComponent` there, starting with its template.
261
284
285
+ a#reactive-component-template
286
+ :marked
262
287
### Component template
263
288
264
- We begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
289
+ Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
265
290
to the `heroForm` property in the component class.
266
291
The `heroForm` is the control model that the component class builds and maintains.
267
292
268
293
+ makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html' ,'form-tag' )( format ='.' )
269
294
:marked
270
- Then we modify the template HTML elements to match the _reactive forms_ style.
295
+ Next, modify the template HTML elements to match the _reactive forms_ style.
271
296
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
272
297
+ makeTabs(
273
298
` cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html,
@@ -277,10 +302,11 @@ a#reactive
277
302
hero-form-template1.component.html (name #2)` )
278
303
279
304
:marked
280
- Key changes:
281
- - the validation attributes are gone (except `required`) because we'll be validating in code.
305
+ Key changes are:
306
+ - The validation attributes are gone (except `required`) because
307
+ validating happens in code.
282
308
283
- - `required` remains, not for validation purposes (we'll cover that in the code),
309
+ - `required` remains, not for validation purposes (that's in the code),
284
310
but rather for css styling and accessibility.
285
311
286
312
.l-sub-section
@@ -289,26 +315,28 @@ a#reactive
289
315
(and perhaps the `aria-required` attribute) when the control has the `required` validator function.
290
316
291
317
Until then, apply the `required` attribute _and_ add the `Validator.required` function
292
- to the control model, as we 'll do below.
318
+ to the control model, as you 'll see below.
293
319
294
320
:marked
295
- - the `formControlName` replaces the `name` attribute; it serves the same
296
- purpose of correlating the input box with the Angular form control.
321
+ - The `formControlName` replaces the `name` attribute; it serves the same
322
+ purpose of correlating the input with the Angular form control.
297
323
298
- - the two-way `[(ngModel)]` binding is gone.
324
+ - The two-way `[(ngModel)]` binding is gone.
299
325
The reactive approach does not use data binding to move data into and out of the form controls.
300
- We do that in code.
326
+ That's all in code.
301
327
302
328
.l-sub-section
303
329
:marked
304
330
The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.
331
+ a#reactive-component-class
305
332
:marked
306
333
### Component class
307
334
308
335
The component class is now responsible for defining and managing the form control model.
309
336
310
- Angular no longer derives the control model from the template so we can no longer query for it.
311
- We create the Angular form control model explicitly with the help of the `FormBuilder`.
337
+ Angular no longer derives the control model from the template so you can no longer query for it.
338
+ You can create the Angular form control model explicitly with
339
+ the help of the `FormBuilder` class.
312
340
313
341
Here's the section of code devoted to that process, paired with the template-driven code it replaces:
314
342
+ makeTabs(
@@ -318,26 +346,27 @@ a#reactive
318
346
` reactive/hero-form-reactive.component.ts (FormBuilder),
319
347
template/hero-form-template2.component.ts (ViewChild)` )
320
348
:marked
321
- - we inject the `FormBuilder` in a constructor.
349
+ - Inject `FormBuilder` in a constructor.
322
350
323
- - we call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview)
324
- because that's when we 'll have the hero data. We'll call it again in the `addHero` method.
351
+ - Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview)
352
+ because that's when you 'll have the hero data. Call it again in the `addHero` method.
325
353
.l-sub-section
326
354
:marked
327
355
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
328
356
:marked
329
- - the `buildForm` method uses the `FormBuilder` ( `fb`) to declare the form control model.
357
+ - The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
330
358
Then it attaches the same `onValueChanged` handler (there's a one line difference)
331
- to the form's `valueChanged ` event and calls it immediately
359
+ to the form's `valueChanges ` event and calls it immediately
332
360
to set error messages for the new control model.
333
361
362
+ a#formbuilder
334
363
:marked
335
364
#### _FormBuilder_ declaration
336
365
The `FormBuilder` declaration object specifies the three controls of the sample's hero form.
337
366
338
367
Each control spec is a control name with an array value.
339
368
The first array element is the current value of the corresponding hero field.
340
- The ( optional) second value is a validator function or an array of validator functions.
369
+ The optional second value is a validator function or an array of validator functions.
341
370
342
371
Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class.
343
372
Angular has stock validators that correspond to the standard HTML validation attributes.
@@ -349,6 +378,7 @@ a#reactive
349
378
:marked
350
379
Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide.
351
380
381
+ a#committing-changes
352
382
:marked
353
383
#### Committing hero value changes
354
384
@@ -357,20 +387,20 @@ a#reactive
357
387
The developer decides _when and how_ to update the data model from control values.
358
388
359
389
This sample updates the model twice:
360
- 1. when the user submits the form
361
- 1. when the user chooses to add a new hero
390
+ 1. When the user submits the form.
391
+ 1. When the user adds a new hero.
362
392
363
- The `onSubmit` method simply replaces the `hero` object with the combined values of the form:
393
+ The `onSubmit() ` method simply replaces the `hero` object with the combined values of the form:
364
394
+ makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' ,'on-submit' )( format ='.' )
365
395
.l-sub-section
366
396
:marked
367
- This example is " lucky" in that the `heroForm.value` properties _just happen_ to
397
+ This example is lucky in that the `heroForm.value` properties _just happen_ to
368
398
correspond _exactly_ to the hero data object properties.
369
399
:marked
370
- The `addHero` method discards pending changes and creates a brand new `hero` model object.
400
+ The `addHero() ` method discards pending changes and creates a brand new `hero` model object.
371
401
+ makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' ,'add-hero' )( format ='.' )
372
402
:marked
373
- Then it calls `buildForm` again which replaces the previous `heroForm` control model with a new one.
403
+ Then it calls `buildForm() ` again which replaces the previous `heroForm` control model with a new one.
374
404
The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
375
405
376
406
Here's the complete reactive component file, compared to the two template-driven component files.
@@ -385,18 +415,18 @@ a#reactive
385
415
386
416
.l-sub-section
387
417
:marked
388
- Run the [live example](#live-example) to see how the reactive form behaves
418
+ Run the [live example](#live-example) to see how the reactive form behaves,
389
419
and to compare all of the files in this cookbook sample.
390
420
391
421
.l-main-section
392
422
a#custom-validation
393
423
:marked
394
424
## Custom validation
395
- This cookbook sample has a custom `forbiddenNamevalidator` function that's applied to both the
425
+ This cookbook sample has a custom `forbiddenNamevalidator() ` function that's applied to both the
396
426
template-driven and the reactive form controls. It's in the `src/app/shared` folder
397
427
and declared in the `SharedModule`.
398
428
399
- Here's the `forbiddenNamevalidator` function itself :
429
+ Here's the `forbiddenNamevalidator() ` function:
400
430
+ makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' ,'custom-validator' , 'shared/forbidden-name.directive.ts (forbiddenNameValidator)' )( format ='.' )
401
431
:marked
402
432
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name
@@ -406,56 +436,72 @@ a#custom-validation
406
436
the validator rejects any hero name containing "bob".
407
437
Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
408
438
409
- The `forbiddenNamevalidator ` factory returns the configured validator function.
439
+ The `forbiddenNameValidator ` factory returns the configured validator function.
410
440
That function takes an Angular control object and returns _either_
411
441
null if the control value is valid _or_ a validation error object.
412
- The validation error object typically has a property whose name is the validation key ( 'forbiddenName')
413
- and whose value is an arbitrary dictionary of values that we could insert into an error message (`{name}`).
442
+ The validation error object typically has a property whose name is the validation key, ` 'forbiddenName'`,
443
+ and whose value is an arbitrary dictionary of values that you could insert into an error message (`{name}`).
414
444
415
- .l-sub-section
416
- :marked
417
- Learn more about validator functions in a _forthcoming_ chapter on custom form validation.
445
+ a#custom-validation-directive
418
446
:marked
419
- #### Custom validation directive
420
- In the reactive forms component we added a configured `forbiddenNamevalidator`
421
- to the bottom of the `'name'` control's validator function list .
447
+ ### Custom validation directive
448
+ In the reactive forms component, the `'name'` control's validator function list
449
+ has a `forbiddenNameValidator` at the bottom .
422
450
+ makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts' ,'name-validators' , 'reactive/hero-form-reactive.component.ts (name validators)' )( format ='.' )
423
451
:marked
424
- In the template-driven component template, we add the selector (`forbiddenName`) of a custom _attribute directive_ to the name's input box
425
- and configured it to reject "bob".
452
+ In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
453
+ of a custom _attribute directive_, which rejects "bob".
426
454
+ makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.html' ,'name-input' , 'template/hero-form-template2.component.html (name input)' )( format ='.' )
427
455
:marked
428
- The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNamevalidator `.
456
+ The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator `.
429
457
430
- Angular forms recognizes the directive's role in the validation process because the directive registers itself
458
+ Angular ` forms` recognizes the directive's role in the validation process because the directive registers itself
431
459
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives.
432
460
+ makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' ,'directive-providers' , 'shared/forbidden-name.directive.ts (providers)' )( format ='.' )
433
461
:marked
434
- The rest of the directive is unremarkable and we present it here without further comment.
462
+ Here is the rest of the directive to help you get an idea of how it all comes together:
435
463
+ makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' ,'directive' , 'shared/forbidden-name.directive.ts (directive)' )
464
+
465
+ :marked
466
+ .l-sub-section
467
+ :marked
468
+ If you are familiar with Angular validations, you may have noticed
469
+ that the custom validation directive is instantiated with `useExisting`
470
+ rather than `useClass`. The registered validator must be _this instance_ of
471
+ the `ForbiddenValidatorDirective`—the instance in the form with
472
+ its `forbiddenName` property bound to “bob". If you were to replace
473
+ `useExisting` with `useClass`, then you’d be registering a new class instance, one that
474
+ doesn’t have a `forbiddenName`.
475
+
476
+ To see this in action, run the example and then type “bob” in the name of Hero Form 2.
477
+ Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
478
+ This time, when you type “bob”, there's no "bob" error message.
479
+
436
480
:marked
437
481
.l-sub-section
438
482
:marked
439
- See the [Attribute Directives](../guide/attribute-directives.html) chapter.
483
+ For more information on attaching behavior to elements,
484
+ see [Attribute Directives](../guide/attribute-directives.html).
440
485
441
486
.l-main-section
442
487
a#testing
443
488
:marked
444
489
## Testing Considerations
445
490
446
- We can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.
491
+ You can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.
447
492
448
493
_Isolated unit tests_ probe the component class directly, independent of its
449
494
interactions with its template, the DOM, other dependencies, or Angular itself.
450
495
451
496
Such tests have minimal setup, are quick to write, and easy to maintain.
452
497
They do not require the `Angular TestBed` or asynchronous testing practices.
453
498
454
- That's not possible with _Template -driven_ forms.
499
+ That's not possible with _template -driven_ forms.
455
500
The template-driven approach relies on Angular to produce the control model and
456
501
to derive validation rules from the HTML validation attributes.
457
502
You must use the `Angular TestBed` to create component test instances,
458
503
write asynchronous tests, and interact with the DOM.
459
504
460
- While not difficult, this takes more time, work and skill —
461
- factors that tend to diminish test code coverage and quality.
505
+ While not difficult, this takes more time, work and
506
+ skill—factors that tend to diminish test code
507
+ coverage and quality.
0 commit comments