Книга: Angular 2 Cookbook
Назад: Bundling FormControls with a FormArray
Дальше: Implementing basic forms with FormBuilder and formControlName

Implementing basic forms with NgForm

The basic denominations of Angular forms are FormControl, FormGroup, and FormArray objects. However, it is often not directly necessary to use these objects at all; Angular provides mechanisms with which you can implicitly create and assign these objects and attach them to the form's DOM elements.

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 {     saveArticle():void {}   }   

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

How to do it...

You should begin by reorganizing this into an actual browser form. Angular gives you a lot of directives and components for this, and importing the FormsModule will give you access to all the ones you need most of the time:

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

In addition, you should reconfigure the button so it becomes an actual submit button. The handler should be triggered when the form is submitted, so you can reattach the listener to the form's native submit event instead of the button's click event. Angular provides an ngSubmit EventEmitter on top of this event, so go ahead and attach the listener to this:

[app/article-editor.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'article-editor',     template: `       <form (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 {     saveArticle():void {}   }   

Next, you should configure the form to pass the form data to the handler through a template variable.

Note

The form element will have an NgForm object (and inside this, a FormGroup) automatically associated with it when you import FormsModule into the encompassing module. Angular creates and associates the NgForm instance behind the scenes.

One way you can access this instance is by assigning the ngForm directive as a template variable. It's a bit of syntactical magic, but using #f="ngForm" signals to Angular that you want to be able to reference the form's NgForm from the template using the f variable.

Once you declare the template variable, you are able to pass the ngForm instance to the submit handler as an argument, specifically as saveArticle(f).

This leaves you with the following:

[app/article-editor.component.ts]     import {Component} from '@angular/core';    import {NgForm} from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form #f="ngForm"             (ngSubmit)="saveArticle(f)">         <p><input placeholder="Article title"></p>         <p><textarea placeholder="Article text"></textarea></p>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     saveArticle(f:NgForm):void {       console.log(f);     }   }   

When you test this manually, you should see your browser logging an NgForm object every time you click on the Save button. Inside this object, you should see a shiny new FormGroup and also the ngSubmit EventEmitter that you are listening to. So far, so good!

Declaring form fields with ngModel

You may have noticed that none of the form fields have been collected. This, of course, is because Angular has not been instructed to pay attention to them. For this, FormsModule provides you with ngModel, which will do certain things for you:

  • Instantiate a FormControl object.
  • Attach it to the DOM element that incorporates the ngModel attribute.
  • Locate the FormGroup that the element lives inside and add to it the FormControl it just created. The string value of the name attribute will be its key inside the FormGroup.

Note

This last bullet is important, as attempting to use ngModel without an encompassing form control construct to attach itself to will result in errors. This form control construct can be the form's FormGroup itself, or it can even be a child FormGroup instance.

With this, go ahead and add ngModel to each of the text input fields:

import {Component} from '@angular/core';   import {NgForm} from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form #f="ngForm"             (ngSubmit)="saveArticle(f)">         <p><input ngModel name="title"                   placeholder="Article title"></p>         <p><textarea ngModel name="text"                      placeholder="Article text"></textarea></p>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     saveArticle(f:NgForm):void {       console.log(f);     }   }   

Your form should now be fully functional. In the submit handler, you can verify that FormGroup has two FormControl objects attached to it by inspecting f.form.controls, which should give you the following:

{     text: FormControl { ... },     title: FormControl { ... }   }   

How it works...

In essence, you are using the hierarchical nature of the DOM to direct how your FormControl architecture is structured. The topmost NgForm instance is coupled with a FormGroup; inside this, the rest of the form's FormControl objects will reside.

Each ngModel directs its referenced FormControl to the FormGroup owned by the NgForm instance. With this nested structure now assembled, it is possible to read and reason the state of the entire form from the NgForm object. This being the case, passing this object to the submit handler will allow you to manage every aspect of form inspection and validation.

There's more...

If, instead, you wanted to group some of these fields together, this can be accomplished by simply wrapping them with an ngModelGroup directive. Similar to ngModel, this automatically instantiates a FormGroup and attaches it to the parent FormGroup; also, it will add any enclosed FormControl or FormGroup objects to itself. For example, refer to the following:

[app/article.component.ts]      import {Component} from '@angular/core';   import {NgForm} from '@angular/forms';      @Component({     selector: 'article-editor',     template: `       <form #f="ngForm"             (ngSubmit)="saveArticle(f)">         <div ngModelGroup="article">           <p><input ngModel                     name="title"                     placeholder="Article title"></p>           <p><textarea ngModel                         name="text"                        placeholder="Article text"></textarea></p>         </div>         <p><button type="submit">Save</button></p>       </form>     `   })   export class ArticleEditorComponent {     saveArticle(f:NgForm):void {       console.log(f);     }   }      

Now, inspecting f.form.controls will reveal that it has a single FormGroup keyed by article:

{     article: FormGroup: {       controls: {         text: FormControl { ... },         title: FormControl { ... }       },       ...     }   }   

Since this matches the structure you set up in the template, it checks out.

See also

  • Implementing simple two-way data binding with ngModel demonstrates the new way in Angular 2 to control bidirectional data flow
  • Implementing basic forms with FormBuilder and formControlName shows how to use the FormBuilder service to quickly put together nested forms
Назад: Bundling FormControls with a FormArray
Дальше: Implementing basic forms with FormBuilder and formControlName

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