As explained previously, a component in an angular form has an instance of FormControl
associated to it. In template driven form, these FormControl
instances are created by angular automatically. A template driven form is created in HTML template and each form component is provided with a directive. Angular then creates FormControl
instances of all those components which have this directive. This directive is ngModel
.
Example, consider a simple login form with two input boxes for user name and password, a checkbox for remember me functionality and a submit button. This form is created with template driver approach in the HTML template and is shown below. Let’s name it as loginform.component.html
<html>
<body>
<form>
<input type=”text” ngModel name=”userName”/>
<input type=”password” ngModel name=”password”/>
<input type=”checkbox” ngModel name=”remember”/>
<button type=”submit”>Submit</button>
</form>
</body>
</html>
If you use ngModel attribute without name attribute, you will get an error like
Error: If ngModel is used within a LoginForm.html:4 forg tag, either the name attribute must be set or the form control must be defined as ‘standalone’ in ngModelOptions.
The error message is pretty clear.
Also, if you use ngModel on an element inside a form, you need to import FormsModule
from '@angular/forms'
package into the imports array of the corresponding module class. FormsModule
provides various functionalities which are required for creating template based forms, one of them is ngModel directive.
Thus, loginform.module.ts file should be modified as below.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { LoginFormComponent } from './loginform.component'; import { FormsModule } from '@angular/forms'; @NgModule({ declarations: [ LoginFormComponent ], imports: [ BrowserModule, FormsModule ], providers: [] }) export class LoginFormModule{ }
If you do not import FormsModule
, then you will get an error at browser console as below.
Error: Template parse errors:
No provider for ControlContainer (”
<html>
<body>
[ERROR ->]<form>….
Submitting template form
In order to submit a template form, following two steps need to be performed:
1. Apply ngForm
directive over the form and assign it to a template variable which references the current form. Angular by default applies ngForm
directive to all forms irrespective of they are applied manually or not but in order to assign it to template reference variable, you need to write it yourself. Also, ngForm
directive contains an event ngSubmit
which is triggered when the form is submitted.
2. Create a method in the component class which should be called when the form is submitted. It is in this method that the form data shall be sent to the server for processing.
3. Associate this method with the ngSubmit
event so that when the form is submitted, the above method gets called. It is the same event explained in step 1.
Modified HTML template for the form with the submit event handled is shown below.
<html>
<body>
<form (ngSubmit) = “submitForm(f)” #f = “ngForm“>
<input type=”text” ngModel name=”userName”/>
<input type=”password” ngModel name=”password”/>
<input type=”checkbox” ngModel name=”remember”/>
<button type=”submit”>Submit</button>
</form>
</body>
</html>
Notice how ngForm
directive is assigned to a template variable and submitForm
method associated with ngSubmit
event in the form tag.
Component class for the above template will be
import { Component } from '@angular/core'; @Component({ selector:'login', templateUrl:'./loginform.component.html', styleUrls: ['./loginform.component.css'] }) export class LoginComponent { onSubmit(f:HTMLFormElement):void { console.log(f.value); // Notice ".value" on form object } }
The above submit event handler just prints the form value at the console. value
field of the form object contains all the elements of the form in key-value format where key is the name
attribute of the form element and value is its value entered by the user.
Thus if user enters “abcd” in user name field, “def” in password field, checks the remember me check box and submits the form, following is the output generated at browser console.
firstName: “abcd”
lastName: “def”
remember: true
Applying Validation to template forms
Validation means checking and verifying the value of a form element before submitting the form as required by the application design. For example, there may be user name which needs to be filled(or should be non-empty), an email field which should contain a valid email address or some field that should not contain special characters.
There are some validations which are standard such as mandatory value, valid e-mail, no special characters, minimum length of value across all applications and some which are specific to an application. These are called custom validations.
Validations are applied to stop invalid values from being submitted to the server. It also includes providing a meaningful message to the user. An end user can enter anything in the input fields and validations are the only way to ensure that correct values are being entered. Angular provides support for applying HTML5 validations as well as custom validations on form elements.
As discussed under Forms section, each form element in angular has a FormControl
object associated to it and a FormControl
has many properties which help us to determine its state. These properties are dirty
, pristine
, touched
, untouched
, valid
and invalid
. These properties also help in applying validation to form controls.
Below is modified HTML template of the above form with required
and minlength
HTML5 validations applied to its name and password input fields respectively.
<html>
<body>
<form (ngSubmit) = “submitForm(f)” #f = “ngForm“>
<input type=”text” ngModel name=”userName” #name=”ngModel” required/>
<!– div containing the error message to be shown when the validation over name field fails–>
<div *ngIf=”name.touched && !name.valid“>Name is required</div>
<input type=”password” ngModel name=”password” #password=”ngModel” minlength/>
<!– div containing the error message to be shown when the validation over name field fails–>
<div *ngIf=”password.touched && !password.valid“>Password should be of minimum 6 characters</div>
<input type=”checkbox” ngModel name=”remember”/>
<button type=”submit”>Submit</button>
</form>
</body>
</html>
There are a few things to notice in the above modified HTML template. They are:
1. required
attribute is applied to the user name field. This is an HTML5 validation that makes this field mandatory.
minlength
attribute is applied on password field and given a value 6. This is also an HTML5 validation which checks if the length of characters entered in the field is not less than the value of minlength
attribute.
2. A template reference variable is added on user name and password fields and ngModel directive is assigned to them. Notice the syntax #name=”ngModel” . This is done because we need a reference to these fields later in the template to apply validations.
3. A div is added below user name and password fields. These divs contain error messages which need to be shown when the values in these fields do not pass the validations. Both these divs contain an ngIf
directive which makes these divs visible only when the conditions in the directive are met. The conditions check if the field has been focused(using touch
attribute of FormControl
object) and its value is valid as per the validation applied to it(using valid
attribute of FormControl
object).
Remember that if touched
attribute is not checked, then the divs will also be visible when the form is first displayed to the user. This will be confusing. Thus, we need to check if the user has visited this field and entered an invalid value.
All the above changes are done to incorporate validations on the form fields. If you do not want any validations, then these changes are not required and the template form will work without these too.
Preventing Form Submission
Earlier we saw how to apply validation on a form and show error messages to the user. But merely showing validation messages will not stop the form from being submitted in case there are validation errors in the form. Form should be submitted only when all its elements contain valid values. But how do we check if all form elements have valid values? One would say check all the elements and if every element has a valid value, then the form should be considered for submission. This would work for smaller forms with few elements but for large forms this would not be the best approach.
Just as FormControl
object has a valid
attribute which tells if the value of this form element is correct as per the validations, there is a valid
attribute for the entire form object. This attribute is true
if all the form elements have valid values, false
otherwise. Angular manages it internally by itself.
Based on the value of valid
attribute of the form, below are two approaches which can be used for preventing form submission when the form data is not correct.
1. Disabling the submit button
Keep the submit button in disabled state when the form loads and enable it only when the form becomes valid. Simply bind the disabled
property of button with the valid
attribute of the form as shown below. Button will be disabled if its disabled
attribute is true
, it will be enabled if this attribute is false
.
<button type=”submit” [disabled] = “!f.valid“>Submit</button>
Note how the disabled
property of button is bound to valid
attribute of form. Here f is the template reference variable which is assigned the form object(refer HTML template above). At the time of page load, valid
attribute will be false
since its form elements do not have correct values as per the applied validations and thus the submit button will be in disabled state. Similarly, if any of the form element does not have a correct value, it will remain in disabled state preventing form submission.
If there are no validations applied, then valid
attribute will be true
at page load time and the submit button will be enabled.
2. Checking form state before submit
This approach also involves the usage of valid attribute of form object. Difference is that it is not checked in the HTML template but in the component class before form data is being sent to the server. Have a look at the modified onSubmit
method in the component class below.
import { Component } from '@angular/core'; @Component({ selector:'login', templateUrl:'./loginform.component.html', styleUrls: ['./loginform.component.css'] }) export class LoginComponent { onSubmit(f:HTMLFormElement):void { // check if form is valid if(f.valid) { // submit the form to server } } }
It checks the valid
attribute of form and submits the form only when the form is valid. Refer above sections to know how the onSubmit
method is provided with the form object.