519 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			519 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
include ../_util-fns
 | 
						||
 | 
						||
a#top
 | 
						||
:marked
 | 
						||
  Improve overall data quality by validating user input for accuracy and completeness.
 | 
						||
 | 
						||
  This cookbook shows how to validate user input in the UI and display useful validation messages
 | 
						||
  using first the template-driven forms and then the reactive forms approach.
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    Read more about these choices in the [Forms](../guide/forms.html)
 | 
						||
    and the [Reactive Forms](../guide/reactive-forms.html) guides.
 | 
						||
 | 
						||
a#toc
 | 
						||
:marked
 | 
						||
  ## Contents
 | 
						||
 | 
						||
    * [Simple template-driven forms](#template1)
 | 
						||
    * [Template-driven forms with validation messages in code](#template2)
 | 
						||
 | 
						||
      * [Component Class](#component-class)
 | 
						||
      * [The benefits of messages in code](#improvement)
 | 
						||
      * [`FormModule` and template-driven forms](#formmodule)
 | 
						||
 | 
						||
    * [Reactive forms with validation in code](#reactive)
 | 
						||
 | 
						||
      * [Switch to the `ReactiveFormsModule`](#reactive-forms-module)
 | 
						||
      * [Component template](#reactive-component-template)
 | 
						||
      * [Component class](#reactive-component-class)
 | 
						||
 | 
						||
        * [`FormBuilder` declaration](#formbuilder)
 | 
						||
        * [Committing hero value changes](#committing-changes)
 | 
						||
 | 
						||
    * [Custom validation](#custom-validation)
 | 
						||
 | 
						||
      * [Custom validation directive](#custom-validation-directive)
 | 
						||
 | 
						||
    * [Testing considerations](#testing)
 | 
						||
 | 
						||
a#live-example
 | 
						||
:marked
 | 
						||
  **Try the live example to see and download the full cookbook source code.**
 | 
						||
live-example(name="cb-form-validation" embedded img="cookbooks/form-validation/plunker.png")
 | 
						||
 | 
						||
.l-main-section
 | 
						||
a#template1
 | 
						||
:marked
 | 
						||
   ## Simple template-driven forms
 | 
						||
 | 
						||
   In the template-driven approach, you arrange
 | 
						||
   [form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
 | 
						||
 | 
						||
   You add Angular form directives (mostly directives beginning `ng...`) to help
 | 
						||
   Angular construct a corresponding internal control model that implements form functionality.
 | 
						||
   In template-drive forms, the control model is _implicit_ in the template.
 | 
						||
 | 
						||
   To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
 | 
						||
   to the elements. Angular interprets those as well, adding validator functions to the control model.
 | 
						||
 | 
						||
   Angular exposes information about the state of the controls including
 | 
						||
   whether the user has "touched" the control or made changes and if the control values are valid.
 | 
						||
 | 
						||
   In this first template validation example,
 | 
						||
   notice the HTML that reads the control state and updates the display appropriately.
 | 
						||
   Here's an excerpt from the template HTML for a single input control bound to the hero name:
 | 
						||
+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='.')
 | 
						||
 | 
						||
:marked
 | 
						||
  Note the following:
 | 
						||
 | 
						||
  * The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
 | 
						||
 | 
						||
  * The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
 | 
						||
  with an Angular form control called `name` in its internal control model.
 | 
						||
 | 
						||
  * The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
 | 
						||
 | 
						||
  * The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
 | 
						||
  This gives you a reference to the Angular `NgModel` directive
 | 
						||
  associated with this control that you can use _in the template_
 | 
						||
  to check for control states such as `valid` and `dirty`.
 | 
						||
 | 
						||
  * The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
 | 
						||
  the control is either `dirty` or `touched`.
 | 
						||
 | 
						||
  * Each nested `<div>` can present a custom message for one of the possible validation errors.
 | 
						||
  There are messages for `required`, `minlength`, and `maxlength`.
 | 
						||
 | 
						||
  The full template repeats this kind of layout for each data entry control on the form.
 | 
						||
a#why-check
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    #### Why check _dirty_ and _touched_?
 | 
						||
 | 
						||
    The app shouldn't show errors for a new hero before the user has had a chance to edit the value.
 | 
						||
    The checks for `dirty` and `touched` prevent premature display of errors.
 | 
						||
 | 
						||
    Learn about `dirty` and `touched` in the [Forms](../guide/forms.html) guide.
 | 
						||
:marked
 | 
						||
  The component class manages the hero model used in the data binding
 | 
						||
  as well as other code to support the view.
 | 
						||
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.ts','class','template/hero-form-template1.component.ts (class)')
 | 
						||
 | 
						||
:marked
 | 
						||
   Use this template-driven validation technique when working with static forms with simple, standard validation rules.
 | 
						||
 | 
						||
   Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach:
 | 
						||
 | 
						||
+makeTabs(
 | 
						||
  `cb-form-validation/ts/src/app/template/hero-form-template1.component.html,
 | 
						||
   cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`,
 | 
						||
  '',
 | 
						||
  `template/hero-form-template1.component.html,
 | 
						||
   template/hero-form-template1.component.ts`)
 | 
						||
 | 
						||
.l-main-section
 | 
						||
a#template2
 | 
						||
:marked
 | 
						||
  ## Template-driven forms with validation messages in code
 | 
						||
 | 
						||
  While the layout is straightforward,
 | 
						||
  there are obvious shortcomings with the way it's handling validation messages:
 | 
						||
 | 
						||
  * It takes a lot of HTML to represent all possible error conditions.
 | 
						||
  This gets out of hand when there are many controls and many validation rules.
 | 
						||
 | 
						||
  * There's a lot of JavaScript logic in the HTML.
 | 
						||
 | 
						||
  * The messages are static strings, hard-coded into the template.
 | 
						||
  It's easier to maintain _dynamic_ messages in the component class.
 | 
						||
 | 
						||
  In this example, you can move the logic and the messages into the component with a few changes to
 | 
						||
  the template and component.
 | 
						||
 | 
						||
  Here's the hero name again, excerpted from the revised template
 | 
						||
  (Template 2), next to the original version:
 | 
						||
+makeTabs(
 | 
						||
  `cb-form-validation/ts/src/app/template/hero-form-template2.component.html,
 | 
						||
   cb-form-validation/ts/src/app/template/hero-form-template1.component.html`,
 | 
						||
  'name-with-error-msg, name-with-error-msg',
 | 
						||
  `hero-form-template2.component.html (name #2),
 | 
						||
   hero-form-template1.component.html (name #1)`)
 | 
						||
 | 
						||
:marked
 | 
						||
  The `<input>` element HTML is almost the same. There are noteworthy differences:
 | 
						||
 | 
						||
  * The hard-code error message `<divs>` are gone.
 | 
						||
 | 
						||
  * There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
 | 
						||
  It invalidates the control if the user enters "bob" in the name `<input>`([try it](#live-example)).
 | 
						||
  See the [custom validation](#custom-validation) section later in this cookbook for more information
 | 
						||
  on custom validation directives.
 | 
						||
 | 
						||
  * The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
 | 
						||
 | 
						||
  * Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
 | 
						||
 | 
						||
a#component-class
 | 
						||
:marked
 | 
						||
  ### Component class
 | 
						||
  The original component code for Template 1 stayed the same; however,
 | 
						||
  Template 2 requires some changes in the component. This section covers the code
 | 
						||
  necessary in Template 2's component class to acquire the Angular
 | 
						||
  form control and compose error messages.
 | 
						||
 | 
						||
  The first step is to acquire the form control that Angular created from the template by querying for it.
 | 
						||
 | 
						||
  Look back at the top of the component template at the
 | 
						||
  `#heroForm` template variable in the `<form>` element:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template1.component.html','form-tag','template/hero-form-template1.component.html (form tag)')(format='.')
 | 
						||
 | 
						||
:marked
 | 
						||
  The `heroForm` variable is a reference to the control model that Angular derived from the template.
 | 
						||
  Tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','view-child','template/hero-form-template2.component.ts (heroForm)')(format='.')
 | 
						||
 | 
						||
:marked
 | 
						||
  Some observations:
 | 
						||
 | 
						||
  * Angular `@ViewChild` queries for a template variable when you pass it
 | 
						||
  the name of that variable as a string (`'heroForm'` in this case).
 | 
						||
 | 
						||
  * The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
 | 
						||
  Periodically inspecting it reveals these changes.
 | 
						||
 | 
						||
  * Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
 | 
						||
  when anything changes in the view.
 | 
						||
  That's the right time to see if there's a new `heroForm` object.
 | 
						||
 | 
						||
  * When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
 | 
						||
  The `onValueChanged` handler looks for validation errors after every keystroke.
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','handler','template/hero-form-template2.component.ts (handler)')(format='.')
 | 
						||
 | 
						||
:marked
 | 
						||
  The `onValueChanged` handler interprets user data entry.
 | 
						||
  The `data` object passed into the handler contains the current element values.
 | 
						||
  The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object.
 | 
						||
 | 
						||
  The `formErrors` is a dictionary of the hero fields that have validation rules and their current error messages.
 | 
						||
  Only two hero properties have validation rules, `name` and `power`.
 | 
						||
  The messages are empty strings when the hero data are valid.
 | 
						||
 | 
						||
  For each field, the `onValueChanged` handler does the following:
 | 
						||
    * Clears the prior error message, if any.
 | 
						||
    * Acquires the field's corresponding Angular form control.
 | 
						||
    * If such a control exists _and_ it's been changed ("dirty")
 | 
						||
    _and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
 | 
						||
 | 
						||
  Next, the component needs some error messages of course—a set for each validated property with
 | 
						||
  one message per validation rule:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.ts','messages','template/hero-form-template2.component.ts (messages)')(format='.')
 | 
						||
:marked
 | 
						||
  Now every time the user makes a change, the `onValueChanged` handler checks for validation errors and produces messages accordingly.
 | 
						||
 | 
						||
a#improvement
 | 
						||
:marked
 | 
						||
  ### The benefits of messages in code
 | 
						||
 | 
						||
  Clearly the template got substantially smaller while the component code got substantially larger.
 | 
						||
  It's not easy to see the benefit when there are just three fields and only two of them have validation rules.
 | 
						||
 | 
						||
  Consider what happens as the number of validated
 | 
						||
  fields and rules increases.
 | 
						||
  In general, HTML is harder to read and maintain than code.
 | 
						||
  The initial template was already large and threatening to get rapidly worse
 | 
						||
  with the addition of more validation message `<div>` elements.
 | 
						||
 | 
						||
  After moving the validation messaging to the component,
 | 
						||
  the template grows more slowly and proportionally.
 | 
						||
  Each field has approximately the same number of lines no matter its number of validation rules.
 | 
						||
  The component also grows proportionally, at the rate of one line per validated field
 | 
						||
  and one line per validation message.
 | 
						||
 | 
						||
  Both trends are manageable.
 | 
						||
 | 
						||
  Now that the messages are in code, you have more flexibility and can compose messages more efficiently.
 | 
						||
  You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
 | 
						||
  In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
 | 
						||
 | 
						||
a#formmodule
 | 
						||
:marked
 | 
						||
  ### _FormModule_ and template-driven forms
 | 
						||
 | 
						||
  Angular has two different forms modules—`FormsModule` and
 | 
						||
  `ReactiveFormsModule`—that correspond with the
 | 
						||
  two approaches to form development. Both modules come
 | 
						||
  from the same `@angular/forms` library package.
 | 
						||
 | 
						||
  You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
 | 
						||
  Here's how you imported it in the `HeroFormTemplateModule`.
 | 
						||
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template.module.ts','','template/hero-form-template.module.ts')(format='.')
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
 | 
						||
    form template in this cookbook.
 | 
						||
 | 
						||
    They're not germane to the validation story. Look at the [live example](#live-example) if you're interested.
 | 
						||
 | 
						||
.l-main-section
 | 
						||
a#reactive
 | 
						||
:marked
 | 
						||
  ## Reactive forms with validation in code
 | 
						||
 | 
						||
  In the template-driven approach, you markup the template with form elements, validation attributes,
 | 
						||
  and `ng...` directives from the Angular `FormsModule`.
 | 
						||
  At runtime, Angular interprets the template and derives its _form control model_.
 | 
						||
 | 
						||
  **Reactive Forms** takes a different approach.
 | 
						||
  You create the form control model in code. You write the template with form elements
 | 
						||
  and `form...` directives from the Angular `ReactiveFormsModule`.
 | 
						||
  At runtime, Angular binds the template elements to your control model based on your instructions.
 | 
						||
 | 
						||
  This approach requires a bit more effort. *You have to write the control model and manage it*.
 | 
						||
 | 
						||
  This allows you to do the following:
 | 
						||
  
 | 
						||
  * Add, change, and remove validation functions on the fly.
 | 
						||
  * Manipulate the control model dynamically from within the component.
 | 
						||
  * [Test](#testing) validation and control logic with isolated unit tests.
 | 
						||
 | 
						||
  The following cookbook sample re-writes the hero form in _reactive forms_ style.
 | 
						||
 | 
						||
a#reactive-forms-module
 | 
						||
:marked
 | 
						||
  ### Switch to the _ReactiveFormsModule_
 | 
						||
  The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
 | 
						||
  The application module for the reactive forms feature in this sample looks like this:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.module.ts','','src/app/reactive/hero-form-reactive.module.ts')(format='.')
 | 
						||
:marked
 | 
						||
  The reactive forms feature module and component are in the `src/app/reactive` folder.
 | 
						||
  Focus on the `HeroFormReactiveComponent` there, starting with its template.
 | 
						||
 | 
						||
a#reactive-component-template
 | 
						||
:marked
 | 
						||
  ### Component template
 | 
						||
 | 
						||
  Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
 | 
						||
  to the `heroForm` property in the component class.
 | 
						||
  The `heroForm` is the control model that the component class builds and maintains.
 | 
						||
 | 
						||
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html','form-tag')(format='.')
 | 
						||
:marked
 | 
						||
  Next, modify the template HTML elements to match the _reactive forms_ style.
 | 
						||
  Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
 | 
						||
+makeTabs(
 | 
						||
  `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.html,
 | 
						||
   cb-form-validation/ts/src/app/template/hero-form-template2.component.html`,
 | 
						||
  'name-with-error-msg, name-with-error-msg',
 | 
						||
  `hero-form-reactive.component.html (name #3),
 | 
						||
   hero-form-template1.component.html (name #2)`)
 | 
						||
 | 
						||
:marked
 | 
						||
  Key changes are:
 | 
						||
  * The validation attributes are gone (except `required`) because
 | 
						||
  validating happens in code.
 | 
						||
 | 
						||
  * `required` remains, not for validation purposes (that's in the code),
 | 
						||
  but rather for css styling and accessibility.
 | 
						||
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    A future version of reactive forms will add the `required` HTML validation attribute to the DOM element
 | 
						||
    (and perhaps the `aria-required` attribute) when the control has the `required` validator function.
 | 
						||
 | 
						||
    Until then, apply the `required` attribute _and_ add the `Validator.required` function
 | 
						||
    to the control model, as you'll see below.
 | 
						||
 | 
						||
:marked
 | 
						||
  * The `formControlName` replaces the `name` attribute; it serves the same
 | 
						||
  purpose of correlating the input with the Angular form control.
 | 
						||
 | 
						||
  * The two-way `[(ngModel)]` binding is gone.
 | 
						||
  The reactive approach does not use data binding to move data into and out of the form controls.
 | 
						||
  That's all in code.
 | 
						||
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.
 | 
						||
a#reactive-component-class
 | 
						||
:marked
 | 
						||
  ### Component class
 | 
						||
 | 
						||
  The component class is now responsible for defining and managing the form control model.
 | 
						||
 | 
						||
  Angular no longer derives the control model from the template so you can no longer query for it.
 | 
						||
  You can create the Angular form control model explicitly with
 | 
						||
  the help of the `FormBuilder` class.
 | 
						||
 | 
						||
  Here's the section of code devoted to that process, paired with the template-driven code it replaces:
 | 
						||
+makeTabs(
 | 
						||
  `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts,
 | 
						||
   cb-form-validation/ts/src/app/template/hero-form-template2.component.ts`,
 | 
						||
  'form-builder, view-child',
 | 
						||
  `reactive/hero-form-reactive.component.ts (FormBuilder),
 | 
						||
   template/hero-form-template2.component.ts (ViewChild)`)
 | 
						||
:marked
 | 
						||
  * Inject `FormBuilder` in a constructor.
 | 
						||
 | 
						||
  * Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview)
 | 
						||
  because that's when you'll have the hero data. Call it again in the `addHero` method.
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
 | 
						||
:marked
 | 
						||
  * The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
 | 
						||
  Then it attaches the same `onValueChanged` handler (there's a one line difference)
 | 
						||
  to the form's `valueChanges` event and calls it immediately
 | 
						||
  to set error messages for the new control model.
 | 
						||
 | 
						||
a#formbuilder
 | 
						||
:marked
 | 
						||
  #### _FormBuilder_ declaration
 | 
						||
  The `FormBuilder` declaration object specifies the three controls of the sample's hero form.
 | 
						||
 | 
						||
  Each control spec is a control name with an array value.
 | 
						||
  The first array element is the current value of the corresponding hero field.
 | 
						||
  The optional second value is a validator function or an array of validator functions.
 | 
						||
 | 
						||
  Most of the validator functions are stock validators provided by Angular as static methods of the `Validators` class.
 | 
						||
  Angular has stock validators that correspond to the standard HTML validation attributes.
 | 
						||
 | 
						||
  The `forbiddenNames` validator on the `"name"` control is a custom validator,
 | 
						||
  discussed in a separate [section below](#custom-validation).
 | 
						||
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    Learn more about `FormBuilder` in the [Introduction to FormBuilder](../guide/reactive-forms.html#formbuilder) section of Reactive Forms guide.
 | 
						||
 | 
						||
a#committing-changes
 | 
						||
:marked
 | 
						||
  #### Committing hero value changes
 | 
						||
 | 
						||
  In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
 | 
						||
  Reactive forms do not use data binding to update data model properties.
 | 
						||
  The developer decides _when and how_ to update the data model from control values.
 | 
						||
 | 
						||
  This sample updates the model twice:
 | 
						||
  
 | 
						||
  1. When the user submits the form.
 | 
						||
  1. When the user adds a new hero.
 | 
						||
 | 
						||
  The `onSubmit()` method simply replaces the `hero` object with the combined values of the form:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts','on-submit')(format='.')
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    This example is lucky in that the `heroForm.value` properties _just happen_ to
 | 
						||
    correspond _exactly_ to the hero data object properties.
 | 
						||
:marked
 | 
						||
  The `addHero()` method discards pending changes and creates a brand new `hero` model object.
 | 
						||
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts','add-hero')(format='.')
 | 
						||
:marked
 | 
						||
  Then it calls `buildForm()` again which replaces the previous `heroForm` control model with a new one.
 | 
						||
  The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
 | 
						||
 | 
						||
  Here's the complete reactive component file, compared to the two template-driven component files.
 | 
						||
+makeTabs(
 | 
						||
  `cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts,
 | 
						||
   cb-form-validation/ts/src/app/template/hero-form-template2.component.ts,
 | 
						||
   cb-form-validation/ts/src/app/template/hero-form-template1.component.ts`,
 | 
						||
  '',
 | 
						||
  `reactive/hero-form-reactive.component.ts (#3),
 | 
						||
   template/hero-form-template2.component.ts (#2),
 | 
						||
   template/hero-form-template1.component.ts (#1)`)
 | 
						||
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    Run the [live example](#live-example) to see how the reactive form behaves,
 | 
						||
    and to compare all of the files in this cookbook sample.
 | 
						||
 | 
						||
.l-main-section
 | 
						||
a#custom-validation
 | 
						||
:marked
 | 
						||
  ## Custom validation
 | 
						||
  This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the
 | 
						||
  template-driven and the reactive form controls. It's in the `src/app/shared` folder
 | 
						||
  and declared in the `SharedModule`.
 | 
						||
 | 
						||
  Here's the `forbiddenNamevalidator()` function:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts','custom-validator', 'shared/forbidden-name.directive.ts (forbiddenNameValidator)')(format='.')
 | 
						||
:marked
 | 
						||
  The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name
 | 
						||
  and returns a validator function.
 | 
						||
 | 
						||
  In this sample, the forbidden name is "bob";
 | 
						||
  the validator rejects any hero name containing "bob".
 | 
						||
  Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
 | 
						||
 | 
						||
  The `forbiddenNameValidator` factory returns the configured validator function.
 | 
						||
  That function takes an Angular control object and returns _either_
 | 
						||
  null if the control value is valid _or_ a validation error object.
 | 
						||
  The validation error object typically has a property whose name is the validation key, `'forbiddenName'`,
 | 
						||
  and whose value is an arbitrary dictionary of values that you could insert into an error message (`{name}`).
 | 
						||
 | 
						||
a#custom-validation-directive
 | 
						||
:marked
 | 
						||
  ### Custom validation directive
 | 
						||
  In the reactive forms component, the `'name'` control's validator function list
 | 
						||
  has a `forbiddenNameValidator` at the bottom.
 | 
						||
+makeExample('cb-form-validation/ts/src/app/reactive/hero-form-reactive.component.ts','name-validators', 'reactive/hero-form-reactive.component.ts (name validators)')(format='.')
 | 
						||
:marked
 | 
						||
  In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
 | 
						||
  of a custom _attribute directive_, which rejects "bob".
 | 
						||
+makeExample('cb-form-validation/ts/src/app/template/hero-form-template2.component.html','name-input', 'template/hero-form-template2.component.html (name input)')(format='.')
 | 
						||
:marked
 | 
						||
  The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator`.
 | 
						||
 | 
						||
  Angular `forms` recognizes the directive's role in the validation process because the directive registers itself
 | 
						||
  with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives.
 | 
						||
+makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts','directive-providers', 'shared/forbidden-name.directive.ts (providers)')(format='.')
 | 
						||
:marked
 | 
						||
  Here is the rest of the directive to help you get an idea of how it all comes together:
 | 
						||
+makeExample('cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts','directive', 'shared/forbidden-name.directive.ts (directive)')
 | 
						||
 | 
						||
:marked
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    If you are familiar with Angular validations, you may have noticed
 | 
						||
    that the custom validation directive is instantiated with `useExisting`
 | 
						||
    rather than `useClass`. The registered validator must be _this instance_ of
 | 
						||
    the `ForbiddenValidatorDirective`—the instance in the form with
 | 
						||
    its `forbiddenName` property bound to “bob". If you were to replace
 | 
						||
    `useExisting` with `useClass`, then you’d be registering a new class instance, one that
 | 
						||
    doesn’t have a `forbiddenName`.
 | 
						||
 | 
						||
    To see this in action, run the example and then type “bob” in the name of Hero Form 2.
 | 
						||
    Notice that you get a validation error. Now change from `useExisting` to `useClass` and try again.
 | 
						||
    This time, when you type “bob”, there's no "bob" error message.
 | 
						||
 | 
						||
:marked
 | 
						||
.l-sub-section
 | 
						||
  :marked
 | 
						||
    For more information on attaching behavior to elements,
 | 
						||
    see [Attribute Directives](../guide/attribute-directives.html).
 | 
						||
 | 
						||
.l-main-section
 | 
						||
a#testing
 | 
						||
:marked
 | 
						||
  ## Testing Considerations
 | 
						||
 | 
						||
  You can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.
 | 
						||
 | 
						||
  _Isolated unit tests_ probe the component class directly, independent of its
 | 
						||
  interactions with its template, the DOM, other dependencies, or Angular itself.
 | 
						||
 | 
						||
  Such tests have minimal setup, are quick to write, and easy to maintain.
 | 
						||
  They do not require the `Angular TestBed` or asynchronous testing practices.
 | 
						||
 | 
						||
  That's not possible with _template-driven_ forms.
 | 
						||
  The template-driven approach relies on Angular to produce the control model and
 | 
						||
  to derive validation rules from the HTML validation attributes.
 | 
						||
  You must use the `Angular TestBed` to create component test instances,
 | 
						||
  write asynchronous tests, and interact with the DOM.
 | 
						||
 | 
						||
  While not difficult, this takes more time, work and
 | 
						||
  skill—factors that tend to diminish test code
 | 
						||
  coverage and quality.
 |