Книга: Angular 2 Cookbook
Назад: Implementing basic forms with NgForm
Дальше: Creating and using a custom validator

Implementing basic forms with FormBuilder and formControlName

Out of the box, Angular provides a way for you to put together forms that don't rely on the template hierarchy for definition. Instead, you can use FormBuilder to explicitly define how you want to structure the form objects and then manually attach them to each input.

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><input placeholder="Article title"></p>       <p><textarea placeholder="Article text"></textarea></p>       <p><button (click)="saveArticle()">Save</button></p>     `   })   export class ArticleEditorComponent {     constructor() {}     saveArticle():void {}   }   

Your objective is to collect all of the form data and submit it using Angular's form constructs.

How to do it...

FormBuilder is included in ReactiveFormsModule, so you will need to import these targets into the application module:

[app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {ReactiveFormsModule} from '@angular/forms';   import {ArticleEditorComponent} from './article-editor.component';      @NgModule({     imports: [       BrowserModule,       ReactiveFormsModule     ],     declarations: [       ArticleEditorComponent      ],     bootstrap: [       ArticleEditorComponent      ]   })   export class AppModule {}   

Additionally, you will need to inject it into your component to make use of it. In Angular 2, this can simply be accomplished by listing it as a typed constructor parameter. The FormBuilder uses the group() method to return the top-level FormGroup, which you should assign to your component instance. For now, you will pass an empty object as its only argument.

With all this, you can integrate the articleGroup FormGroup into the template by attaching it inside a form tag using the formGroup directive:

[app/article-editor.component.ts]      import {Component, Inject} from '@angular/core';   import {FormBuilder, FormGroup} from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form [formGroup]="articleGroup"             (ngSubmit)="saveArticle()">         <p><input placeholder="Article title"></p>         <p><textarea placeholder="Article text"></textarea></p>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     articleGroup:FormGroup;        constructor(@Inject(FormBuilder) formBuilder:FormBuilder) {       this.articleGroup = formBuilder.group({});     }     saveArticle():void {}   }   

With all this, you have successfully created the structure for your form, but FormGroup is still not connected to the multiple input. For this, you will first set up the structure of the controls inside the builder and consequently attach them to each input tag with formControlName, as follows:

[app/article-editor.component.ts]      import {Component, Inject} from '@angular/core';   import {FormBuilder, FormGroup, Validators} from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form [formGroup]="articleGroup"             (ngSubmit)="saveArticle()">         <p><input formControlName="title"                    placeholder="Article title"></p>         <p><textarea formControlName="text"                      placeholder="Article text"></textarea></p>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     articleGroup:FormGroup;        constructor(@Inject(FormBuilder) formBuilder:FormBuilder) {       this.articleGroup = formBuilder.group({         title: [null, Validators.required],         text: [null, Validators.required]       });     }     saveArticle():void {       console.log(this.articleGroup);     }   }   

With this, your form will have two FormControl objects instantiated inside it, and they will be associated with proper input elements. When you click on Submit, you will be able to see the input FormControls inside FormGroup. However, you may prefer to namespace these FormControl objects inside an article designation, and you can easily do this by introducing an ngFormGroup and a corresponding level of indirection inside the formBuilder definition:

[app/article-editor.component.ts]      import {Component, Inject} from '@angular/core';   import {FormBuilder, FormGroup, Validators} from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form [formGroup]="articleGroup"             (ngSubmit)="saveArticle()">         <div formGroupName="article">           <p><input formControlName="title"                      placeholder="Article title"></p>           <p><textarea formControlName="text"                        placeholder="Article text"></textarea></p>         </div>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     articleGroup:FormGroup;          constructor(@Inject(FormBuilder) formBuilder:FormBuilder) {       this.articleGroup = formBuilder.group({         article: formBuilder.group({           title: [null, Validators.required],           text: [null, Validators.required]         })       });     }     saveArticle():void {       console.log(this.articleGroup);     }   }   

Now, the title and text FormControl objects will exist nested inside an article FormGroup and they can be successfully validated and inspected in the submit handler.

How it works...

As you might suspect, the arrays living inside the formBuilder.group definitions will be applied as arguments to a FormControl constructor. This is nice since you can avoid the new FormControl() boilerplate when creating each control. The string that keys the FormControl is linked to it with formControlName. Because you are using formControlName and formGroupName, you will need to have the formBuilder nested structure match exactly to what is there in the template.

There's more...

It is totally understandable that having to duplicate the structure in the template and the FormBuilder definition is a little annoying. This is especially true in this case, as the presence of formGroup doesn't really add any valuable behavior since it is attached to an inert div element. Instead, you might want to be able to do this article namespace grouping without modifying the template. This behavior can be accomplished with formControl, whose behavior is similar to formModel (it binds to an existing instance on the component).

Note

Note the paradigm that is being demonstrated with these different kinds of form directives. With things such as ngForm, formGroup, formArray, and formControl, Angular is implicitly creating and linking these instances. If you choose to not use FormBuilder to define how FormControls behave, this can be accomplished by adding validation directives to the template. On the other hand, you also have formModel and formControl, which bind to the instances of these control objects that you must manually create on the component.

[app/article-editor.component.ts]      import {Component, Inject} from '@angular/core';   import {FormBuilder, FormControl, FormGroup, Validators}      from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form [formGroup]="articleGroup"             (ngSubmit)="saveArticle()">         <p><input [formControl]="titleControl"                    placeholder="Article title"></p>         <p><textarea [formControl]="textControl"                      placeholder="Article text"></textarea></p>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     titleControl:FormControl        = new FormControl(null, Validators.required);     textControl:FormControl        = new FormControl(null, Validators.required);     articleGroup:FormGroup;          constructor(@Inject(FormBuilder) formBuilder:FormBuilder) {       this.articleGroup = formBuilder.group({         article: formBuilder.group({           title: this.titleControl,           text: this.textControl         })       });     }     saveArticle():void {       console.log(this.articleGroup);     }   }       

Importantly, note that you have created an identical output of the one you created earlier. title and text are bundled inside an article FormGroup. However, the template doesn't need to have any reference to this intermediate FormGroup.

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
  • Implementing basic forms with ngForm demonstrates Angular's declarative form construction
Назад: Implementing basic forms with NgForm
Дальше: Creating and using a custom validator

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