"intro":"User input triggers DOM events. We listen to those events with EventBindings that funnel updated values back into our components and models."
},
"forms":{
"title":"Forms",
"intro":"A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors."
},
"pipes":{
"title":"Pipes",
"intro":"Pipes transform displayed values within a template"
@ -29,11 +34,6 @@
"intro":"How to write templates that display data and consume user events with the help of data binding."
},
"forms":{
"title":"Angular 2 Forms",
"intro":"Learn about the different approaches we can take when building forms and see examples of them in action."
},
"dependency-injection":{
"title":"Dependency Injection",
"intro":"Angular's dependency injection system creates and delivers dependent services \"just-in-time\"."
We’ve all used a form to login, submit a help request, place an order, book a flight,
schedule a meeting and perform countless other data entry tasks.
Forms are the mainstay of business applications.
Any seasoned web developer can slap together an HTML form with all the right tags.
It's more challenging to create a cohesive data entry experience that guides the
user efficiently and effectively through the workflow behind the form.
*That* takes design skills that are, to be frank, well out of scope for this chapter.
It also takes framework support for
**two-way data binding, change tracking, validation, and error handling**
... which we shall cover in this chapter on Angular forms.
We will build a simple form from scratch, one step at a time. Along the way we'll learn
- how to build an Angular form with a component and template
- the `ng-model` two-way data binding syntax for reading and writing values to input controls
- the `ng-control` directive to track the change state and validity of form controls
- the special CSS classes that `ng-control` adds to form controls and how we can use them to provide strong visual feedback
- how to display validation errors to users and enable/disable form controls
- how to share information across controls with template local variables
.l-main-section
:markdown
Forms play an important role in Web applications whether it’s collecting data or allowing data to be modified or deleted. We’ve all used a form to login to an application, submit a help request to a website, manipulate business data and more. While creating custom forms in HTML is a relatively straightforward process, the complexity increases when a form requires data binding, event handling, validation rules and error messages.
## Template-Driven Forms
Angular 2 provides several key features that can be used to build any type of form your application may need whether it’s a large enterprise line of business form or a small form used to log a user into an application. Throughout this chapter we’ll learn about the different approaches we can take when building forms and see examples of them in action.
Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
the form-specific Directives and techniques described in this chapter.
.l-sub-section
:markdown
That's not the only way to create a form but it's the way we'll cover in this chapter.
:markdown
We can build almost any form we need with an Angular template ... login forms, contact forms ... pretty much any business forms.
We 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.
The overall learning goals of this chapter include:
It will be pretty easy because Angular handles many of the repeative, boiler plate tasks we'd
otherwise wrestle with ourselves.
- Learn how to build an Angular 2 form component and template.
- Learn data binding syntax that can be used to minimize the amount of code you write when building forms.
- Learn about key Angular 2 directives that can be used in forms.
- Learn how to display errors to users and enable/disable form controls.
- Learn what template local variables are and how they can be used.
- Learn about CSS classes that Angular 2 adds to form controls and understand how they can be used to provide visual feedback to users.
Let’s get started by taking a look at the process we can follow to create template-driven forms in Angular 2.
## Introduction to Template-Driven Forms
We can use template-driven forms to create login forms, contact forms and custom line of business forms in our Angular 2 applications. We can create any type of form layout, bind data to form controls, display validation errors, disable specific form controls when a form is invalid, provide visual feedback to users, plus more. In scenarios where we need to capture data from end users or allow them to modify existing data, template-driven forms provide a robust option that can be created quickly and easily using Angular 2 features.
Here’s an example of a simple template-driven form in action. We’ll discuss the form throughout this chapter and learn how to build it.
We'll discuss and learn to build the following template-driven form:
Here’s the same form displaying a validation error and providing additional visual feedback to the end user:
Here at the *Hero Employment Agency* we use this form to maintain personal information about the
heroes in our stable. Every hero needs a job. It's our company mission to match the right hero with the right crisis!
Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot.
If we delete the hero name, the form displays a validation error in an attention grabbing style:
figure.image-display
img(src="/resources/images/devguide/forms/tdf-2.png" alt="Invalid, Name Required")
img(src="/resources/images/devguide/forms/hf-2.png" alt="Invalid, Name Required")
:markdown
If the user doesn’t satisfy all of the validation requirements for the form, the submit button will automatically be disabled and the left portion of the input control that has an error will change from green to red. The colors used, location of the colors and more can be customized using standard CSS. Required controls have a red left-border to make them easy to visually identify for end users:
Note that the submit button is disabled and the "required" bar to the left of the input control changed from green to red.
p We'll' customize the colors and location of the "required" bar with standard CSS.
:markdown
What features does Angular 2 provide that we can use to create this type of template-driven form? We’ll answer this question and others by walking through the following steps:
We will build this form in the following sequence of small steps
1. Create an HTML form template
1. Create a model class
1. Create a form component
1. Add the **ng-submit** directive to the form
1. Add the **ng-control** directive to each form input control
1. Create the `Hero` model class
1. Create the component that controls the form
1. Create a template with the initial form layout
1. Add the **ng-model** directive to each form input control
1. Disable the form’s submit button until the form is valid
1. Add the **ng-control** directive to each form input control
1. Add custom CSS to provide visual feedback
1. Show and hide validation error messages
1. Handle form submission with **ng-submit**
1. Disable the form’s submit button until the form is valid
The following sections provide a more detailed look at these steps and the code that we’ll need to write to make it all happen. Let’s start by looking at the initial form template.
.l-main-section
:markdown
## Create the Hero Model Class
### Create an HTML Form Template
As users enter form data, we capture their changes and update an instance of a model.
We can't layout the form until we know what the model looks like.
We can use a standard HTML form with the Angular template-driven approach. An example of a form template named **template-form.component.html** is shown next. The HTML content includes the form element and associated inputs as well as a section after the form that’s used to display data submitted by the end user.
A model can be as simple as a "property bag" that holds facts about a thing of application importance.
That describes well our `Hero` class with its three required fields (`id`, `name`, `power`)
Create a new file called `hero.ts` and give it the following class definition:
+makeExample('forms/ts/src/app/hero.ts')
:markdown
The form captures Name, Alter Ego and Power values and makes all three of the inputs required by using the **required** attribute. The select control in the form iterates through an array of powers and displays them as select options using the Angular 2 **ng-for** directive.
The application switches between the form view and the data display view by toggling a **submitted** property that we’ll discuss later in this chapter. The **submitted** property is bound to the **hidden** property of the div containers that wrap the form section and data display section.
Now that the template for the form is in place, we can move on to discussing the model object that is bound to the form.
## Create a Model Class
As users interacts with the form we’ll capture the data they enter or modify in instances of a “model” class. A model class is simply a class that holds application data. In this example **Hero** is the model class and it has **id**, **name**, **power**, and **alterEgo** properties.
Here’s the complete code for the Hero model (hero.ts):
It's an anemic model with few requirements and no behavior. Perfect for our demo.
The TypeScript compiler generates a public field for each `public` constructor parameter and
assigns the parameter’s value to that field automatically when we create new heroes like this:
```
export class Hero {
constructor(
public id?: number,
public name?: string,
public power?: string,
public alterEgo?: string
) { }
}
let myHero = new Hero(42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover');
console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
```
The TypeScript compiler generates a public field for each **public** constructor parameter and assigns the parameter’s value to that field automatically. For example, TypeScript generates an **alterEgo** field to match the **public alterEgo** constructor parameter and sets the alterEgo field to the optional value of the fourth argument. This is a convenient way to initialize an object through the constructor with minimal code.
Let’s create a form component that uses this **Hero** model class.
The `alterEgo` is optional and the constructor lets us omit it; note the (?) in `alterEgo?`.
.l-main-section
:markdown
## Create a Form component
The HTML form template shown earlier is placed directly in an Angular component using the **View** annotation’s **template** property. Alternatively, we can create a separate file and link it to the component using the **View** annotation’s **templateUrl** property. Which approach is better? There’s no “right” answer since it’s very subjective, but in cases where a template contains a lot of HTML content, having a separate template file can be helpful and minimize the overall size of the component.
An Angular form has two parts: an HTML-based template and a code-based Component to handle data and user interactions.
In this example we’ll link the template to a component named **TemplateFormComponent (template-form.component.ts)** using the **templateUrl** property of **View** annotation. The complete code for **TemplateFormComponent** is shown next:
```
import {Component, View, NgFor, FORM_DIRECTIVES} from 'angular2/angular2';
We begin with the Component because it states, in brief, what the Hero editor can do.
import { Hero } from './hero';
Create a new file called `hero-form.component.ts` and give it the following definition:
There’s nothing special about this component, nothing to distinguish it from any component we've written before,
nothing form-specific about it ... except, perhaps, the tell-tale `FORM_DIRECTIVES` import.
model: Hero;
powers: string[];
submitted: boolean = false;
Understanding this component requires only the Angular 2 concepts we’ve learned in previous chapters
constructor() {
this.model = new Hero(18,'Dr IQ', 'Really Smart', 'Chuck Overstreet');
1. We import a standard set of symbols from the Angular library.
We don't have a template yet but we usually import `CORE_DIRECTIVES` and it doesn't surprise us to
import something called `FORM_DIRECTIVES`, given that we'll be writing a form
this.powers = ['Really Smart', 'Super Flexible',
'Super Hot', 'Weather Changer'];
}
1. The `@Component` selector value of "hero-form" means we can drop this form in a parent template with a `<hero-form>` tag.
onSubmit() {
this.submitted = true;
}
1. The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`.
}
```
We can see that there’s nothing unique about this component even though we’re using it to create a form. Many of the Angular 2 concepts we’ve already learned apply. Here are few of the key features shown in the component:
1. We defined dummy data for `model` and `powers` as befits a demo.
Down the road, we can inject a data service to get and save real data
or perhaps expose these properties as [inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a
parent component. None of this concerns us now and these future changes won't affect our form.
1. We import **NgFor, Component, View** and **FORM_DIRECTIVES** as well as the the **Hero** model class. Ng
1. We add the **Component** selector value of “template-driven-form”. We also define the form template **(template-form.component.html)** using the **templateUrl** property and all of the directives we’ll use in the form template using the **directives** property.
1. The **TemplateFormComponent** class **model, powers** and **submitted** properties <span style="background-color:pink" >defined</span>.
1. The constructor initializes the model and powers properties with values.
1. We threw in a `diagnostic` property at the end to return a JSON representation of our model.
It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
We’ve now linked the form template to the form component and integrated the **Hero** model class into the component. The next step is to integrate Angular 2 directives into the form.
We may wonder why we aren't writing the template inline in the component file as we have often done
elsewhere in the Developer Guide.
## Add the ng-submit Directive to the Form
There is no “right” answer for all occasions. We kind of like inline templates when they are short.
Most form templates won't be short. TypeScript and JavaScript files generally aren't the best place to
write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code.
We also like short files with a clear and obvious purpose like this one.
We’ll now shift our attention back to the form template **(template-form.component.html)** created earlier. Although the form is linked to the component, it won’t do much at this point aside from showing the different form controls. Angular 2 directives have to be added into the form so that we can bind the Hero model and perform other tasks such as validation.
We made a good choice to put the HTML template elsewhere. Let's write it.
To start, we can add the **ng-submit** event to the **form** element and hook it to the **onSubmit()** function shown earlier in the **TemplateFormComponent class**:
```
<form (ng-submit)="onSubmit()" #f="form">
```
it’s important to note that **form** is a directive in this case that exposes an **ng-submit** event. We’re using the event binding syntax in this example to wire **ng-submit** to **onSubmit()**. The **#f** attribute (which is referred to as a “template local variable”) makes the form object available as a local variable named “f” within the template. We’ll be using the local variable later to handle disabling the submit button when the form isn’t valid.
.l-main-section
:markdown
## Create an initial HTML Form Template
Now that we’ve added the ability to submit the form, let’s discuss different form directives that can be added to the form’s input controls.
Create a new template file called `hero-form.component.html` and give it the following definition:
That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and
opening them up for user input in input boxes.
Angular 2 provides several key directives named **ng-model** and **ng-control** that can be used in forms to handle data binding and validation/change tracking tasks respectively. Both directives can be used together to simplify the overall process of creating custom forms and capturing and storing input from end users.
The "Name" `<input>` control has the HTML5 `required` attribute;
the "Alter Ego" `<input>` control does not because `alterEgo` is optional.
The **ng-control** directive provides support for tracking the state of controls (**dirty, errors, pristine, touched** and **untouched**) and determining if they’re valid or not.
We've got a "Submit" button at the bottom with some classes on it.
is modified by an end user, **ng-control** updates its state properties as the user enters data a control so that we can know when a control is dirty, pristine, touched or has errors.
**We are not using Angular yet**. There are no bindings. No extra directives. Just layout.
The **ng-control** directive also provides support for checking the validity of a form control through its **valid** property. For example, the **valid** property of a form input control that has with an HTML5 **required** attribute added to it will have its is false until the user doesn’t supplies a value. This can be useful when checking the validity of an individual form control or the form as a whole.
The `container`,`form-group`, `form-control`, and `btn` classes are CSS Bootstrap. Purely cosmetic.
We're using Bootstrap to gussy up our form.
Hey, what's a form without a little style!
Here’s an example of the **ng-control** directive applied to a textbox:
```
<input type="text"
ng-control="alterEgo"
required>
```
In this example we’re assigning the **ng-control** directive to a string value of “alterEgo”. By adding the directive to a form input control we automatically get access to the properties discussed earlier (dirty, pristine, etc.) and also get programmatic access to the form control which can be useful for custom validation scenarios. Later in this chapter we’ll see how the value assigned to **ng-control** can be used to show users error messages when they don’t satisfy the requirements of a given form control.
.l-sub-section
:markdown
Since we're using [CSS Boostrap](http://getbootstrap.com/css/).
now might be a good time to install it into our project.
We can do that with npm.
## Add the ng-model Directive
Open a terminal window and enter the command:
code-example(language="html" escape="html").
npm install bootstrap
:markdown
<br>Open the `index.html` and add the following line wherever we like to put our CSS
The **ng-control** directive can be combined with another directive called **ng-model** on form controls. The **ng-model** directive can be used to bind data to form controls and also listen for changes to the data through an **ng-model** event. This is useful anytime we need to bind a model object property to textbox, select, radio button or other form input control.
.callout.is-important
header Angular Forms Does Not Require A Style Library
:markdown
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
the styles of any external library. We are welcome to use the CSS library we choose
... or none at all.
Here’s an example of using the **ng-control** and **ng-model** directives as well as handling the **ng-model** event on a textbox:
```
<input type="text"
ng-control="alterEgo"
[ng-model]="model.alterEgo"
(ng-model)="model.alterEgo=$event"
required>
```
In this example we’re using the standard Angular 2 [property-name] binding syntax to bind the **ng-model** directive to the model object’s **alterEgo** property. We’re also listening for an event named **ng-model** using the standard (event-name) event binding syntax. When the **ng-model** event fires, the value of the input control is passed using the **$event** object and assigned to the **model.alterEgo** property. This pattern of listening for property changes through an event provides an efficient way to *emulate* the two-way data binding mechanism that Angular 1.x provided while allowing Angular 2 to provide several performance benefits.
.l-main-section
:markdown
## Add Powers with ***ng-for**
Our hero may choose one super power from a fixed list of Agency-approved powers.
We maintain that list internally (in `HeroFormComponent`).
In scenarios where the **ng-model** event handler must perform additional work aside from simply updating the property, we can define it separately using the standard (event-name) syntax and then bind it to a component event handler function:
```
<input type="text"
ng-control="alterEgo"
[ng-model]="model.alterEgo"
(ng-model)="myEventHandler($event)"
required>
```
When we use **[ng-model]** and **(ng-model)** it may seem strange that the directive and event are named the same. Why wouldn’t they have different names to make it easier to distinguish them from one another in the HTML template?
We'll add a `select` to our
form and bind the options to the `powers` list using `NgFor`,
a technique we might have seen before in the ["Displaying Data"](./displaying-data.html) chapter.
The answer is that Angular 2 provides an alternative syntax that merges the **ng-model** directive and event concepts together into one attribute on the input element which reduces the amount of code that has to be written. This merging of the property binding and event binding syntax can be accomplished using the **[(ng-model)]** short-cut syntax.
Add the following HTML *immediately below* the "Alter Ego" group.
The [property-name] portion of the syntax handles the property binding while the (event-name) portion handles the event binding. The directive automatically updates the target property as the **ng-model** event fires allowing us to eliminate the **(ng-model)=”model.alterEgo=$event”** syntax shown earlier.
:markdown
We are repeating the `<options>` tag for each power in the list of Powers.
The `#p` local template variable is a different power in each iteration;
we display its name using the interpolation syntax with the double-curly-braces.
Here’s an example of using the more concise **[(ng-model)]** syntax to simplify the **ng-model** code shown earlier:
```
<input type="text"
ng-control="alterEgo"
[(ng-model)]="model.alterEgo"
required>
```
The **[(ng-model)]** code accomplishes the same task as the previous code that defined the directive and event separately. By using the shortcut binding syntax we’re able to handle property and event bindings in a single step.
It’s important to note that this shortcut syntax is optional and not something that we’re required to use. Just as the **[property-name]** shortcut syntax can be replaced with **bind-property-name** and **(event-name)** can be replaced with **on-event**-name (referred to as the “canonical syntax”) in Angular 2 templates, we can also use a canonical syntax in place of the **[(ng-model)]** syntax if desired. The canonical style uses the **bindon-property-name** syntax in place of the **[(ng-model)]** shortcut syntax:
```
<input type="text"
ng-control="alterEgo"
bindon-ng-model="model.alterEgo"
required>
```
Angular 2 allows us to choose the syntax that we prefer (shortcut or canonical) with all template bindings which makes it flexible to work with and also allows teams to decide on their own unique coding style.
## Disabling the Submit Button
The final step in the template-driven form that we’ve been building throughout this chapter involves disabling the submit button when the form is invalid. After all, why would we want the user to be able to click the button when we know the form isn’t ready to process yet?
Earlier we defined the form using the following syntax:
```
<form (ng-submit)="onSubmit()" #f="form">
```
The **#f** attribute creates a template local variable that can be used to access the form object elsewhere in the template. By checking the form’s **valid** property we can easily determine if the form is valid or not. By using **f.form.valid** we can disable the submit button by binding to the **disabled** property:
```
<button type="submit"
class="btn btn-default"
[disabled]="!f.form.valid">
Submit
</button>
```
## Add Custom CSS to Provide Visual Feedback
Earlier in the chapter we saw that form controls can be modified to show the user when a given control is valid or invalid as they interact with the form. For example, when a form first loads we can mark required controls with a red border if no data is present to provide the user with a visual indicator:
.l-main-section
:markdown
## Two-way data binding with ***ng-model**
We might be disappointed if we ran the app right now.
We appended a diagnostic interpolation after the input tag
so we can see what we're doing.
We left ourselves a note to throw it way when we're done.
:markdown
As the user interacts with the form and satisfies a control’s requirements we can change from a red border to a green border to let them “visually” know how they’re doing:
Focus on the binding syntax: `[(ng-model)]="..."`.
If we ran the app right now and started typing in the "Name" input box,
adding and deleting characters, we'd see them appearing and disappearing
from the interpolated text.
At some point it might look like this.
figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action.png" alt="ng-model in action")
:markdown
The diagnostic is evidence that we really are flowing values from the input box to the model and
back again. **That's two-way data binding!**
Let's add similar `[(ng-model)]` bindings to "Alter Ego" and "Hero Power".
We'll ditch the input box binding message
and add a new binding at the top to the component's `diagnostic` property.
Then we can confirm that we are in fact two-way data binding *to the entire Hero model*.
After revision the core of our form should have three `[(ng-model)]` bindings that
Before discussing how to accomplish this task, let’s discuss features that form controls expose when they have **ng-control** or **ng-model** directives applied to them. When a control has either of these directives the control provides access to properties such as **pristine, dirty, touched, untouched** and **valid**. As these properties change, CSS classes are added to the control automatically by the aforementioned directives. We can use the CSS classes that are added to dynamically show visual feedback to the end user.
Let’s walk through the CSS classes that change as the user interacts with the **name** control in the form. When the form first loads the **Hero** model data is bound into the control. The following CSS classes appear on the input control due to the control having an initial value:
<img src="/resources/images/devguide/forms/name-control-1.png" alt="CSS class initial value">
We achieve this effect by adding two CSS styles to our `styles.css` file
that select for the Angular validity classes and the HTML 5 "required" attribute:
+makeExample('forms/ts/src/styles.css')
:markdown
The key CSS classes added to the control in this example include **ng-pristine, ng-valid** and **ng-untouched**. The presence of the **ng-pristine** class let’s us know that the user hasn’t interacted with the control yet, the **ng-valid** class appears because the control is valid and **ng-untouched** appears because the user hasn’t interacted with the control yet.
## Show and Hide Validation Error messages
If the user clear’s out the value of the **name** control the CSS classes change to the following:
We can do better.
<img src="/resources/images/devguide/forms/name-control-2.png" alt="CSS class initial value">
In this case the **ng-touched, ng-dirty** and **ng-invalid** classes have been added. The presence of the **ng-touched** class let’s us know that the user has interacted with the control, **ng-dirty** let’s us know that the control’s value has changed and **ng-invalid** let’s us know that the control isn’t currently valid.
Now that we know the classes that are added to the control we can modify our site’s CSS stylesheet and handle valid and invalid control states to provide visual feedback in the form for the end user. For example, when the control is valid we can add the following CSS class to change the control’s left-border to green:
```
.ng-valid {
border-left: 5px solid #42A948;
}
```
When the control is invalid we can add the following CSS class to change the left-border to red:
```
.ng-invalid {
border-left: 5px solid #a94442;
}
```
By using the CSS classes automatically added by **ng-control** and **ng-model** we can easily customize a form and display different visual feedback as input controls are modified by the end user.
## Show and Hide Validation Error Messages
All of the controls in the form have the HTML5 **required** attribute applied to them. While we’ve taken care of disabling the submit button when the form isn’t valid, we need a way to display an error message to the end user for each invalid control.
To do this we first need to create a local variable on the control and assign it to a value of form. Here’s an example of creating two local variables that are named **#name** and **#alterego**:
Once each input has a local variable assigned we can access the variable elsewhere in the template to determine if the control is valid or not. If a control is invalid an error message can be shown to the end user. How does this work exactly?
Since each input has a local variable defined we can use it to access a **control** property. This property allows us to get to information about the control including whether or not it’s valid. Here’s an example of using the **#name** local variable to access the **valid** property:
code-example(language="html").
Valid: {{ name.valid }}
:markdown
In this example we’re accessing the desired form control through the **#name** local variable. It exposes properties such as **valid, pristine, dirty** and **touched**. Here’s an example of binding the **hidden** property of multiple div elements that contain an error message to the **valid** property of a form control:
There's our hero again, displayed read-only with interpolation bindings.
This slug of HTML only appears while the component is in the submitted state.
There's an "Edit" button whose click event we bound to an expression
that clears the `submitted` flag.
Click it and this block disappears and the editable form reappears.
That's as much drama as we can muster for now.
.l-main-section
:markdown
## Conclusion
The Angular 2 form discussed in this chapter takes advantage of the following framework features to provide support for data modification, validation and more:
- An HTML form template.
- A form component class with **Component** and **View** annotations.
- The **ng-submit** event for handling the form submission.
- Template local variables such as **#f, #name, #alterego** and **#power**.
- The **ng-control** and **ng-model** directives.
- The **[(ng-model)]** shortcut syntax to bind the directive to a model object property and handle property updates as data changes in the form.
- HTML5 validation attributes such as **required**.
- The local variable’s **control** property on input controls to check if a control is valid and show/hide error messages.
- Angular 2 property binding shortcut syntax to disable the submit button when the form is invalid.
- Custom CSS classes that provide visual feedback to end users about valid and invalid controls.
- An Angular HTML form template.
- A form component class with a `Component` decorator.
- The `ng-submit` directive for handling the form submission.
- Template local variables such as `#hf`, `#name`, `#alter-ego` and `#power`.
- The `ng-model` directive for two-way data binding.
- The `ng-control` for validation and form element change tracking.
- The local variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
- Property Binding to disable the submit button when the form is invalid.
- Custom CSS classes that provide visual feedback to users about required invalid controls.
Here’s the final version of the form template that includes all of these framework features:
Here’s the final version of the application includes all of these framework features: