Forms are the mainstay of business applications. You use forms to log in, submit a help request, place an order, book a flight, schedule a meeting, and perform countless other data-entry tasks.
In developing a form, it’s important to create a data-entry experience that guides the user efficiently and effectively through the workflow.
Developing forms requires design skills (which are out of scope for this page), as well as framework support for two-way data binding, change tracking, validation, and error handling, which you’ll learn about on this page.
This page shows you how to build a simple form from scratch. Along the way you’ll learn how to:
- Build an Angular form with a component and template.
ngModelto create two-way data bindings for reading and writing input-control values.
- Track state changes and the validity of form controls.
- Provide visual feedback using special CSS classes that track the state of the controls.
- Display validation errors to users and enable/disable form controls.
- Share information across HTML elements using template reference variables.
You can run the
You can build forms by writing templates in the Angular template syntax with the form-specific directives and techniques described in this page.
You can build almost any form with an Angular template—login forms, contact forms, and pretty much any business form. You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors, conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you’d otherwise wrestle with yourself.
You’ll learn to build a template-driven form that looks like this:
The Hero Employment Agency uses this form to maintain personal information about heroes. Every hero needs a job. It’s the company mission to match the right hero with the right crisis.
Two of the three fields on this form are required. Following material design guidelines, required fields have an asterisk (*).
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
Note that the Submit button is disabled, and the input control changes from green to red.
You’ll build this form in small steps:
- Create the
- Create the component that controls the form.
- Create a template with the initial form layout.
- Bind data properties to each form control using the ngModel two-way data-binding syntax.
- Add an ngControl directive to each form-input control.
- Add custom CSS to provide visual feedback.
- Show and hide validation-error messages.
- Handle form submission with ngSubmit.
- Disable the form’s Submit button until the form is valid.
Follow the setup instructions to create a new project named
+ angular_forms: ^1.0.0
Create a model
As users enter form data, you’ll capture their changes and update an instance of a model. You can’t lay out the form until you know what the model looks like.
A model can be as simple as a “property bag” that holds facts about a thing of importance for the app.
That describes well the
Hero class with its three required fields (
and one optional field (
lib directory, create the following file with the given content:
It’s an anemic model with few requirements and no behavior, good enough for the demo.
alterEgo is optional, so the constructor lets you omit it;
note the brackets in
You can create a new hero like this:
Create a basic form
An Angular form has two parts: an HTML-based template and a component class to handle data and user interactions programmatically. Begin with the class because it states, in brief, what the hero editor can do.
Create a form component
Create the following file with the given content:
There’s nothing special about this component, nothing form-specific, nothing to distinguish it from any component you’ve written before.
Understanding this component requires only the Angular concepts covered in previous pages.
- The code imports the main Angular library and the
Heromodel you just created.
@Componentselector value of
hero-formmeans you can drop this form in a parent template with a
templateUrlproperty points to a separate file (which you’ll create shortly) for the template HTML.
You defined mock data for
Revise the app component
AppComponent is the app’s root component. It will host the
Replace the contents of the starter app version with the following:
Create an initial form template
Create the template file with the following contents:
The language is simply HTML5. You’re presenting two of the
opening them up for user input in input boxes.
<input> control has the HTML5
the Alter Ego
<input> control does not because
alterEgo is optional.
You added a Submit button at the bottom with some classes on it for styling.
You’re not using Angular yet. There are no bindings or extra directives, just layout.
In template driven forms, if you’ve imported the
angular_forms library, you don’t have to do anything
<form> tag in order to make use of the library capabilities. Continue on to see how this works.
open_in_browser Refresh the browser. You’ll see a simple, unstyled form.
Style the form
Angular makes no use of Bootstrap classes or the styles of any external library. Angular apps can use any CSS library or none at all.
Add Bootstrap styles by inserting the following link to the
open_in_browser Refresh the browser. You’ll see a form with style!
Add powers with *ngFor
The hero must choose one superpower from a fixed list of agency-approved powers.
You maintain that list internally (in
You’ll add a
select to the
form and bind the options to the
powers list using
a technique seen previously in the Displaying Data page.
Add the following HTML immediately below the Alter Ego group:
This code repeats the
<option> tag for each power in the list of powers.
p template input variable is a different power in each iteration;
you display its name using the interpolation syntax.
Two-way data binding with ngModel
open_in_browser Running the app now is a bit disappointing.
You don’t see hero data because you’re not binding to the
You know how to do that from earlier pages.
Displaying Data teaches property binding.
User Input shows how to listen for DOM events with an
event binding and how to update a component property with the displayed value.
Now you need to display, listen, and extract at the same time.
You could use the techniques you already know, but
instead you’ll use the new
[(ngModel)] syntax, which
makes binding the form to the model easy.
<input> tag for Name and update it like this:
You added a diagnostic interpolation before the form-group so you can see what you’re doing. You left yourself a note to throw it away when you’re done.
Focus on the binding syntax:
open_in_browser Run the app now and type in the Name input, adding and deleting characters. You’ll see the characters appear and disappear from the diagnostic text. At some point it might look like this:
The diagnostic is evidence that values really are flowing from the input to the model and back again.
Notice that you also added an
ngControl directive to the
<input> tag and set it to “name”,
which makes sense for the hero’s name. Any unique value will do, but using a descriptive name is helpful.
ngControl directive is a requirement when using
[(ngModel)] in combination with a form.
Internally, Angular creates
NgFormControl instances and
registers them with an
NgForm directive that Angular attached to the
NgFormControl is registered under the name you assigned to the
You’ll read more about
NgForm later in this guide.
[(ngModel)] bindings and
ngControl directives to Alter Ego and Hero Power.
Replace the diagnostic binding expression with
model. This way you can
confirm that two-way data binding works for the entire hero model.
After revision, the core of the form should look like this:
- Each input element has an
idproperty that is used by the
forattribute to match the label to its input control.
- Each input element has a
ngControldirective that is required by Angular forms to register the control with the form.
If you run the app now and change every hero model property, the form might display like this:
The diagnostic near the top of the form confirms that all of your changes are reflected in the model.
Delete the diagnostic binding from the template since it has served its purpose.
Give visual feedback based on control state
Using CSS and class bindings, you can change a form control’s appearance to reflect its state.
Track control state
An Angular form control can tell you if the user touched the control, if the value changed, or if the value became invalid.
Each control (NgControl) in an Angular form tracks its own state and makes the state available for inspection through the following field members:
pristineindicate whether the control’s value has changed.
untouchedindicate whether the control has been visited.
validreflects the control value’s validity.
valid control property is the most interesting, because you want to send a
strong visual signal when a control value is invalid.
To create such visual feedback, you’ll use the
Bootstrap custom-forms classes
Temporarily add another template reference variable named
to the Name
<input> tag and use it to display the input’s CSS classes.
Template reference variables
Why “ngForm”? A Directive’s
exportAs property tells Angular
how to link the reference variable to the directive. You set
name to “ngForm”
because the ngModel
exportAs property is “ngForm”.
open_in_browser Refresh the browser, and follow these steps:
- Look at the Name input.
- It has a green border.
- Its has the classes
- Change the name by adding some characters. The classes remain the same.
- Delete the name.
- The input box border turns red.
#spy template reference variable and the diagnostic that uses it.
As an alternative to class bindings, you can use an NgClass directive to style a control. First, add the following method to set a control’s state-dependent CSS class names:
Show and hide validation error messages
You can improve the form. The Name input is required, and clearing it turns the box outline red. That says something is wrong but the user doesn’t know what is wrong or what to do about it. Leverage the control’s state to reveal a helpful message.
Use the valid and pristine states
When the user deletes the name, the form should look like this:
To achieve this effect, add the following
<div> immediately after the Name
open_in_browser Refresh the browser and delete the Name input. The error message is displayed.
You control visibility of the error message by setting the hidden attribute
<div> based on the state of the
In this example, you hide the message when the control is valid or pristine — “pristine” means the user hasn’t changed the value since it was displayed in this form.
User experience is the developer’s choice
Some developers want the message
to display at all times. If you ignore the
pristine state, you would hide the
message only when the value is valid. If you arrive in this component with a
new (blank) hero or an invalid hero, you’ll see the error message immediately,
before you’ve done anything.
Some developers want the message to display only when the user makes an invalid change. Hiding the message while the control is “pristine” achieves that goal. You’ll see the significance of this choice when you add a Clear button to the form.
The hero Alter Ego is optional so you can leave that be.
Hero Power selection is required. You can add the same kind of error message
<select> if you want, but it’s not imperative because the selection box
already constrains the power to valid values.
Add a Clear button
clear() method to the component class:
Add a Clear button with a
click event binding, right after the Submit button:
open_in_browser Refresh the browser. Click the Clear button. The text fields go blank, and if you’ve changed the power, it reverts to its default value.
Submit the form with ngSubmit
The user should be able to submit this form after filling it in.
The Submit button at the bottom of the form
does nothing on its own, but it will
trigger a form submit because of its type (
A form submit is useless at the moment. To make it useful, assign
onSubmit() method to the form’s
ngSubmit event binding:
Note the template reference variable
As was explained earlier,
heroForm gets bound to the
NgForm directive that governs the form as a whole.
Angular automatically creates and attaches an NgForm directive to the
NgForm directive supplements the
form element with additional features.
It holds the controls you created for the elements with
and monitors their properties, including their validity.
You’ll bind the form’s overall validity via
heroForm variable to the button’s
open_in_browser Refresh the browser. You’ll find that the button is enabled—although it doesn’t do anything useful yet.
Now if you delete the Name, you violate the “required” rule, which is duly noted in the error message. The Submit button is also disabled.
Not impressed? Think about it for a moment. What would you have to do to wire the button’s enable/disabled state to the form’s validity without Angular’s help?
For you, it was as simple as this:
- Define a template reference variable on the (enhanced) form element.
- Refer to that variable in a button many lines away.
Display the model (optional)
Submitting the form has no visual effect at the moment.
As can be expected for a demo. Jazzing up the demo won’t teach you anything new about forms. But this is an opportunity to exercise some of your newly won binding skills. If you aren’t interested, skip to this page’s summary.
As a visual effect, you can hide the data entry area and display something else.
Wrap the form in a
<div> and bind
hidden property to the
The form is visible from the start because the
submitted property is false until you submit the form,
as this fragment from the
Now add the following HTML below the
<div> wrapper you just wrote:
Refresh the browser, and submit the form.
submitted flag becomes true and the form disappears.
You’ll see the hero model values (read-only) displayed in a table.
The view includes an Edit button whose click event binding clears the
When you click the Edit button, the table disappears and the editable form reappears.
Angular forms provide support for data modification, validation, and more. In this page, you learned how to use the following features:
- An HTML form template, and a form component class with an
- Form submission, handled through an
- Template reference variables, such as
- Two-way data bindings (
NgControldirectives for validation and form-element change tracking.
validproperty of input controls (accessed through template reference variables), for checking control validity and showing/hiding error messages.
- NgForm.form validity to set the Submit button’s enabled state.
- Custom CSS classes to provide visual feedback to users about control state.
The final project folder structure should look like this:
Here’s the code for the final version of the app: