Книга: Angular 2 Cookbook
Назад: Implementing basic field validation with a FormControl
Дальше: Bundling FormControls with a FormArray

Bundling controls with a FormGroup

Naturally, forms in applications frequently exist to aggregate multiple instances of input into a unified behavior. One common behavior is to assess whether a form is valid, which of course requires that all of its subfields are valid. This will most commonly be achieved by bundling multiple FormControl objects into a FormGroup. This can be done in different ways, with varying degrees of explicitness. This recipe covers an entirely explicit implementation, that is, everything here will be created and "joined" manually.

Note

The code, links, and a live example related to this recipe are available at .

Getting ready

Suppose you began with the following skeleton application:

[app/article-editor.component.ts]      import {Component} from '@angular/core';       @Component({     selector: 'article-editor',     template: `       <p>Title: <input></p>       <p>Text: <input></p>       <p><button (click)="saveArticle()">Save</button></p>       <hr />       <p>Preview:</p>       <div style="border:1px solid #999;margin:50px;">         <h1>{{article.title}}</h1>         <p>{{article.text}}</p>       </div>     `   })   export class ArticleEditorComponent {     article:{title:string, text:string} = {};        saveArticle():void {}   }   

Your goal is to update the article object (and consequently the template) only if all the input fields are valid.

How to do it...

First, add the necessary code to attach new FormControl objects to each input field and validate them with the built-in required validator:

[app/article-editor.component.ts]      import {Component} from '@angular/core';   import {FormControl, Validators}      from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <p>Title: <input [formControl]="titleControl"></p>       <p>Text: <input [formControl]="textControl"></p>       <p><button (click)="saveArticle()">Save</button></p>       <hr />       <p>Preview:</p>       <div style="border:1px solid #999;margin:50px;">         <h1>{{article.title}}</h1>         <p>{{article.text}}</p>       </div>     `   })   export class ArticleEditorComponent {     article:{title:string, text:string} = {};     titleControl:FormControl        = new FormControl(null, Validators.required);     textControl:FormControl        = new FormControl(null, Validators.required);        saveArticle():void {}   }   

At this point, you could individually inspect each input's FormControl object and check whether it is valid. However, if this form grows to 100 fields, it would become unbearably tedious to maintain them. Therefore, you can bundle these FormControl objects into a single FormGroup instead:

[app/article-editor.component.ts]      import {Component} from '@angular/core';   import {FormControl, FormGroup, Validators}      from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <p>Title: <input [formControl]="titleControl"></p>       <p>Text: <input [formControl]="textControl"></p>       <p><button (click)="saveArticle()">Save</button></p>       <hr />       <p>Preview:</p>       <div style="border:1px solid #999;margin:50px;">         <h1>{{article.title}}</h1>         <p>{{article.text}}</p>       </div>     `   })   export class ArticleEditorComponent {     article:{title:string, text:string} = {};     titleControl:FormControl        = new FormControl(null, Validators.required);     textControl:FormControl        = new FormControl(null, Validators.required);     articleFormGroup:FormGroup = new FormGroup({       title: this.titleControl,       text: this.textControl     });        saveArticle():void {}   }   

FormGroup objects also expose valid and value members, so you can use these to verify and assign directly from the object:

[app/article-editor.component.ts]      import {Component} from '@angular/core';   import {FormControl, FormGroup, Validators}      from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <p>Title: <input [formControl]="titleControl"></p>       <p>Text: <input [formControl]="textControl"></p>       <p><button (click)="saveArticle()">Save</button></p>       <hr />       <p>Preview:</p>       <div style="border:1px solid #999;margin:50px;">         <h1>{{article.title}}</h1>         <p>{{article.text}}</p>       </div>     `   })   export class ArticleEditorComponent {     article:{title:string, text:string} = {};     titleControl:FormControl        = new FormControl(null, Validators.required);     textControl:FormControl        = new FormControl(null, Validators.required);     articleFormGroup:FormGroup = new FormGroup({       title: this.titleControl,       text: this.textControl     });        saveArticle():void {       if (this.articleFormGroup.valid) {         this.article = this.articleFormGroup.value;       } else {         alert("Missing field(s)!");     }   }   

With this addition, your form should now be working fine.

How it works...

Both FormControl and FormGroup inherit from the abstract base class called AbstractControl. What this means for you is that both of them expose the same base class methods, but FormGroup will aggregate its composition of AbstractControl objects to be read from its own members. As you can see in the preceding code, valid acts as a logical AND operator for all the children (meaning every single child must return true for it to return true); value returns an object of the same topology as the one provided at the instantiation of FormGroup, but with each FormControl value instead of the FormControl object.

Note

As you might expect, since FormGroup expects an object with AbstractControl properties, you are free to nest a FormGroup inside another FormGroup.

There's more...

You are able to access a FormGroup's contained FormControl members via the controls property. The string that you used to key the FormControl members—either upon FormGroup instantiation, or with the addControl method—is used to retrieve it. In this example, the text FormControl object could be retrieved inside a component method via this.articleCtrlGroup.controls.text.

Tip

The Angular documentation warns you to specifically not to modify the underlying FormControl collection directly. This may lead to an undefined data binding behavior. So, always be sure to use the FormGroup member methods addControl and removeControl instead of directly manipulating the collection of FormControl objects that you pass upon instantiation.

FormGroup validators

Like Control, a FormGroup can have its own validators. These can be provided when the FormGroup is instantiated, and they behave in the same way that a FormControl validator would behave. By adding validators at the FormGroup level, FormGroup can override the default behavior of only being valid when all its components are valid or adding extra validation clauses.

Error propagation

Angular validators not only have the ability to determine whether they are valid or not, but they are also capable of returning error messages describing what is wrong. For example, when the input fields are empty, if you were to examine the errors property of the text FormControl object via this.articleCtrlGroup.controls.text.errors, it would return {required: true}. This is the default error message of the built-in required validator. However, if you were to inspect the errors property on the parent FormGroup via this.articleCtrlGroup.errors, you will find it to be null.

This may be counter-intuitive, but it is not a mistake. Error messages will only appear on the FormControl instance that is causing them. If you wish to aggregate error messages, you will have to traverse the nested collections of FormControl objects manually.

See also

  • Implementing simple two-way data binding with ngModel demonstrates the new way in Angular 2 to control bidirectional data flow
  • Implementing basic field validation with a FormControl details the basic building block of an Angular form
  • Bundling FormControls with a FormArray shows how to handle iterable form elements
Назад: Implementing basic field validation with a FormControl
Дальше: Bundling FormControls with a FormArray

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already