In this chapter, we will introduce how to build an Angular form using components and templates.
Using Angular templates, we can create various types of forms, such as login forms, contact forms, product detail forms, etc. We can also add data validation to the fields of these forms.
Next, let's implement the form functionality step by step.
* * *
## Creating the Project
Import and initialize the project.
For complete project creation, refer to: (#)
Or download the source code directly:
After unzipping, rename the directory to `angular-forms`. Modify the **"name": "angular-quickstart"** in the `angular-forms/package.json` file to **"name": "angular-forms"**.
After completion, we execute **cnpm install** to load the dependencies.
### Creating the Site Model
Below is a simple model class `Site`, which includes three required fields: `id`, `name`, `url`, and one optional field: `alexa`.
Create a `site.ts` file in the `angular-forms/app` directory with the following code:
## app/site.ts file:
```typescript
export class Site {
constructor(
public id: number,
public name: string,
public url: string,
public alexa?: number
) {}
}
In the code above, the fields marked as `public` are public fields. Adding a question mark (?) after `alexa` indicates it is an optional field.
### Creating a Form Component
Each Angular form consists of two parts: an HTML-based template and a code-based component that handles data and user interactions.
Create a `site-form.component.ts` file in the `angular-forms/app` directory with the following code:
## app/site-form.component.ts file:
```typescript
import { Component } from '@angular/core';
import { Site } from './site';
@Component({
moduleId: module.id,
selector: 'site-form',
templateUrl: 'site-form.component.html'
})
export class SiteFormComponent {
urls = [
'www.',
'www.google.com',
'www.taobao.com',
'www.facebook.com'
];
model = new Site(1, 'Tutorial', this.urls, 10000);
submitted = false;
onSubmit() {
this.submitted = true;
}
get diagnostic() {
return JSON.stringify(this.model);
}
}
The example imports the `Component` decorator and the `Site` model.
The `@Component` selector "site-form" indicates that we can inject this form into a parent template using a **** tag.
The `templateUrl` property points to a separate HTML template file named `site-form.component.html`.
The `diagnostic` property is used to return the JSON representation of the model.
### Defining the Application's Root Module
Modify `app.module.ts` to define the application's root module. The module specifies the external modules it references and declares the components that belong to this module, such as `SiteFormComponent`.
Because template-driven forms have their own module, we need to add `FormsModule` to the `imports` array of this application so that we can use forms.
The code for the `app/app.module.ts` file is as follows:
## app/app.module.ts file:
```typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { SiteFormComponent } from './site-form.component';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [AppComponent, SiteFormComponent],
bootstrap:
})
export class AppModule {}
### Creating the Root Component
Modify the root component file `app.component.ts` to include the `SiteFormComponent`.
## app/app.component.ts file:
```typescript
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: ''
})
export class AppComponent {}
### Creating an Initial HTML Form Template
Create the template file `site-form.component.html` with the following code:
## app/site-form.component.html file:
Website Form
The `required` attribute sets the field as required. If not set, it is optional.
In the `angular-forms` directory, enter the following command:
```bash
cnpm install bootstrap --save
Open the `index.html` file and add the following stylesheet link to the ``:
After running **npm start**, visit: http://localhost:3000/. The output effect is as follows:
!(#)
* * *
## Using ngModel for Two-Way Data Binding
Next, we use `ngModel` for two-way data binding, which updates the component's properties by listening to DOM events.
Modify `app/site-form.component.html` to bind our form to the model using `ngModel`. The code is as follows:
## app/site-form.component.html file:
Website Form
{{diagnostic}}
{{p}}
* Each `input` element has an `id` attribute, which is used by the `label` element's `for` attribute to match the label to the corresponding input.
* Each `input` element has a `name` attribute, which Angular's form module uses to register the controller for the form.
Running the above example produces the following output:
!(#)
**{{diagnostic}}** is only used for testing to output data.
We can also track modification status and validation using `ngModel`. It uses three CSS classes to update the control to reflect the current state.
| State | Class when true | Class when false |
| --- | --- | --- |
| Control has been visited | `ng-touched` | `ng-untouched` |
| Control value has changed | `ng-dirty` | `ng-pristine` |
| Control value is valid | `ng-valid` | `ng-invalid` |
This allows us to add custom CSS to reflect the form's state.
Create a `forms.css` file in the `angular-forms` directory with the following code:
## forms.css file:
```css
.ng-valid, .ng-valid.required {
border-left: 5px solid #42A948;
}
.ng-invalid:not(form) {
border-left: 5px solid #a94442;
}
Open the `index.html` file and add the following stylesheet link to the ``:
Modify `app/site-form.component.html` as follows:
## app/site-form.component.html file:
Website Form
{{diagnostic}}
Website Name is required
{{p}}
In the template, by binding the `hidden` property of the `div` element to the properties of the `name` control, we can control the visibility of the "name" field's error message.
After deleting the data in the name field, the display result is as follows:
!(#)
### Adding a Website
Next, we create a form for adding a website. Add a button in `app/site-form.component.html`:
## app/site-form.component.html file:
Bind the above button event to a component method:
## app/site-form.component.ts file:
```typescript
active = true;
newSite() {
this.model = new Site(5, '', '');
this.active = false;
setTimeout(() => this.active = true, 0);
}
We add an `active` flag to the component and initialize it to `true`. When we add a new website, it sets the `active` flag to `false`, then quickly sets it back to `true` using a quick `setTimeout` function.
### Submitting the Form via ngSubmit
We can use Angular's `NgSubmit` directive to submit the form and bind it to the `SiteFormComponent.submit()` method via event binding.
We define a template reference variable `#siteForm` and initialize it to "ngForm".
This `siteForm` variable now references the `NgForm` directive, which represents the entire form.
The complete code for the `site-form.component.ts` file is as follows:
## app/site-form.component.ts file:
```typescript
import { Component } from '@angular/core';
import { Site } from './site';
@Component({
moduleId: module.id,
selector: 'site-form',
templateUrl: 'site-form.component.html'
})
export class SiteFormComponent {
urls = [
'www.',
'www.google.com',
'www.taobao.com',
'www.facebook.com'
];
model = new Site(1, 'Tutorial', this.urls, 10000);
submitted = false;
onSubmit() {
this.submitted = true;
}
get diagnostic() {
return JSON.stringify(this.model);
}
active = true;
newSite() {
this.model = new Site(5, '', '');
this.active = false;
setTimeout(() => this.active = true, 0);
}
}
The complete code for `app/site-form.component.html` is as follows:
## app/site-form.component.html file:
Website Form
{{diagnostic}}
Website Name is required
{{p}}
You submitted the following information:
Website Name
{{ model.name }}
Website Alexa Rank
{{ model.alexa }}
Website URL
{{ model.url }}
In the template, we bind the `hidden` property to the `SiteFormComponent.submitted` property.
The main form is visible from the beginning because the `submitted` property is `false`. When we submit the form, it is hidden because the `submitted` property becomes `true`:
```typescript
submitted = false;
onSubmit() {
this.submitted = true;
}
The final directory structure is:
!(#)
The source code used in this article can be downloaded in the following way, excluding the `node_modules` and `typings` directories.
(#)
The complete example demonstration GIF is as follows:
!(#)