Revert "docs(Guide): Content edits in the Angular 2 Guide. (#3159)"

This reverts commit 01497865f8.

this breaks harp compilation
This commit is contained in:
Jules Kremer 2017-03-01 14:36:41 -08:00
parent 01497865f8
commit b97fa49eea
2 changed files with 336 additions and 356 deletions

View File

@ -9,14 +9,13 @@ block includes
- var _decoratorLink = '<a href="#' + _decorator + '">' + _decorator + '</a>'
:marked
Angular has its own vocabulary.
Most Angular terms are common English words
Angular has a vocabulary of its own.
Most Angular terms are everyday English words
with a specific meaning within the Angular system.
This glossary lists the most prominent terms
and a few less familiar ones that have unusual or
unexpected definitions.
<!-- CF: The content in this intro seems self evident. Is the intro necessary? -->
[A](#A) [B](#B) [C](#C) [D](#D) [E](#E) [F](#F) [G](#G) [H](#H) [I](#I)
[J](#J) [K](#K) [L](#L) [M](#M) [N](#N) [O](#O) [P](#P) [Q](#Q) [R](#R)
@ -29,7 +28,7 @@ a#aot
## Ahead-of-time (AOT) compilation
.l-sub-section
:marked
You can compile Angular applications at build time.
You can compile Angular applications at build-time.
By compiling your application<span if-docs="ts"> using the compiler-cli, `ngc`</span>, you can bootstrap directly
to a<span if-docs="ts"> module</span> factory, meaning you don't need to include the Angular compiler in your JavaScript bundle.
Ahead-of-time compiled applications also benefit from decreased load time and increased performance.
@ -42,10 +41,10 @@ a#aot
Helps you organize an application into cohesive blocks of functionality.
An Angular module identifies the components, directives, and pipes that the application uses along with the list of external Angular modules that the application needs, such as `FormsModule`.
Every Angular application has an application root-module class. By convention, the class is
Every Angular application has an application root module class. By convention, the class is
called `AppModule` and resides in a file named `app.module.ts`.
For details and examples, see the [Angular Modules (NgModule)](!{docsLatest}/guide/ngmodule.html) page.
For details and examples, see the [Angular Module](!{docsLatest}/guide/ngmodule.html) page.
+ifDocsFor('ts|dart')
:marked
@ -65,7 +64,7 @@ a#attribute-directives
other HTML elements, attributes, properties, and components. They are usually represented
as HTML attributes, hence the name.
For example, you can use the `ngClass` directive to add and remove CSS class names.
A good example of an attribute directive is the `ngClass` directive for adding and removing CSS class names.
Learn about them in the [_Attribute Directives_](!{docsLatest}/guide/attribute-directives.html) guide.
@ -76,10 +75,10 @@ a#attribute-directives
## Barrel
.l-sub-section
:marked
A way to *roll up exports* from several ES2015 modules into a single convenient ES2015 module.
A barrel is a way to *rollup exports* from several ES2015 modules into a single convenient ES2015 module.
The barrel itself is an ES2015 module file that re-exports *selected* exports of other ES2015 modules.
For example, imagine three ES2015 modules in a `heroes` folder:
Imagine three ES2015 modules in a `heroes` folder:
code-example.
// heroes/hero.component.ts
export class HeroComponent {}
@ -90,7 +89,7 @@ a#attribute-directives
// heroes/hero.service.ts
export class HeroService {}
:marked
Without a barrel, a consumer needs three import statements:
Without a barrel, a consumer would need three import statements:
code-example.
import { HeroComponent } from '../heroes/hero.component.ts';
import { Hero } from '../heroes/hero.model.ts';
@ -110,18 +109,18 @@ a#attribute-directives
.alert.is-important
:marked
You can often achieve the same result using [Angular modules](#angular-module) instead.
Note that you can often achieve this using [Angular modules](#angular-module) instead.
:marked
## Binding
.l-sub-section
:marked
Usually refers to [data binding](#data-binding) and the act of
Almost always refers to [Data Binding](#data-binding) and the act of
binding an HTML object property to a data object property.
Sometimes refers to a [dependency-injection](#dependency-injection) binding
between a "token"&mdash;also referred to as a "key"&mdash;and a dependency [provider](#provider).
When using this more rare usage, be clear in context.
May refer to a [dependency injection](#dependency-injection) binding
between a "token", also referred to as a "key", and a dependency [provider](#provider).
This more rare usage should be clear in context.
:marked
## Bootstrap
@ -131,7 +130,7 @@ a#attribute-directives
You launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`). Bootstrapping identifies an application's top level "root" [component](#component), which is the first component that is loaded for the application.
For more information, see the [Setup](!{docsLatest}/guide/setup.html) page.
:marked
You can bootstrap multiple apps in the same `index.html`, each app with its own top-level root.
You can bootstrap multiple apps in the same `index.html`, each with its own top level root.
.l-main-section#C
:marked
@ -141,10 +140,10 @@ a#attribute-directives
The practice of writing compound words or phrases such that each word or abbreviation begins with a capital letter
_except the first letter, which is lowercase_.
Function, property, and method names are typically spelled in camelCase. For example, `square`, `firstName`, and `getHeroes`. Notice that `square` is an example of how you write a single word in camelCase.
Function, property, and method names are typically spelled in camelCase. Examples include: `square`, `firstName` and `getHeroes`. Notice that `square` is an example of how you write a single word in camelCase.
camelCase is also known as *lower camel case* to distinguish it from *upper camel case*, or [PascalCase](#pascalcase).
In Angular documentation, "camelCase" always means *lower camel case*.
This form is also known as **lower camel case**, to distinguish it from **upper camel case**, which is [PascalCase](#pascalcase).
When you see "camelCase" in this documentation it always means *lower camel case*.
a#component
:marked
@ -156,9 +155,9 @@ a#component
The *component* is one of the most important building blocks in the Angular system.
It is, in fact, an Angular [directive](#directive) with a companion [template](#template).
Apply the `!{_at}Component` !{_decoratorLink} to
You apply the `!{_at}Component` !{_decoratorLink} to
the component class, thereby attaching to the class the essential component metadata
that Angular needs to create a component instance and render the component with its template
that Angular needs to create a component instance and render it with its template
as a view.
Those familiar with "MVC" and "MVVM" patterns will recognize
@ -170,7 +169,7 @@ a#component
.l-sub-section
:marked
The practice of writing compound words or phrases such that each word is separated by a dash or hyphen (`-`).
This form is also known as kebab-case.
This form is also known as [kebab-case](#kebab-case).
[Directive](#directive) selectors (like `my-app`) <span if-docs="ts">and
the root of filenames (such as `hero-list.component.ts`)</span> are often
@ -181,18 +180,17 @@ a#component
.l-sub-section
:marked
Applications display data values to a user and respond to user
actions (such as clicks, touches, and keystrokes).
actions (clicks, touches, keystrokes).
In data binding, you declare the relationship between an HTML widget and data source
and let the framework handle the details.
Data binding is an alternative to manually pushing application data values into HTML, attaching
Instead of manually pushing application data values into HTML, attaching
event listeners, pulling changed values from the screen, and
updating application data values.
updating application data values, you can use data binding by declaring the relationship between an HTML widget and data source and let the
framework handle the details.
Angular has a rich data-binding framework with a variety of data-binding
Angular has a rich data binding framework with a variety of data binding
operations and supporting declaration syntax.
Read about the following forms of binding in the [Template Syntax](!{docsLatest}/guide/template-syntax.html) page:
Read about the forms of binding in the [Template Syntax](!{docsLatest}/guide/template-syntax.html) page:
* [Interpolation](!{docsLatest}/guide/template-syntax.html#interpolation).
* [Property binding](!{docsLatest}/guide/template-syntax.html#property-binding).
* [Event binding](!{docsLatest}/guide/template-syntax.html#event-binding).
@ -210,14 +208,14 @@ a#component
.l-sub-section
block decorator-defn
:marked
A *function* that adds metadata to a class, its members (properties, methods) and function arguments.
A decorator is a **function** that adds metadata to a class, its members (properties, methods) and function arguments.
Decorators are a JavaScript language [feature](https://github.com/wycats/javascript-decorators), implemented in TypeScript and proposed for ES2016 (also known as ES7).
Decorators are a JavaScript language [feature](https://github.com/wycats/javascript-decorators), implemented in TypeScript and proposed for ES2016 (AKA ES7).
To apply a decorator, position it immediately above or to the left of the item it decorates.
To apply a decorator, position it immediately above or to the left of the thing it decorates.
Angular has its own set of decorators to help it interoperate with your application parts.
The following example is a `@Component` decorator that identifies a
Here is an example of a `@Component` decorator that identifies a
class as an Angular [component](#component) and an `@Input` decorator applied to the `name` property
of that component. The elided object argument to the `@Component` decorator would contain the pertinent component metadata.
```
@ -229,7 +227,7 @@ a#component
```
The scope of a decorator is limited to the language feature
that it decorates. None of the decorations shown here will "leak" to other
classes that follow it in the file.
classes appearing below it in the file.
.alert.is-important
:marked
@ -239,10 +237,7 @@ a#component
## Dependency injection
.l-sub-section
:marked
<!-- CF: Since there is a whole other page covering this subject,
could this glossary definition be edited down to be much more brief? -->
A design pattern and mechanism
Dependency injection is both a design pattern and a mechanism
for creating and delivering parts of an application to other
parts of an application that request them.
@ -251,26 +246,23 @@ a#component
These parts often rely on other parts. An Angular [component](#component)
part might rely on a service part to get data or perform a calculation. When
part "A" relies on another part "B," you say that "A" depends on "B" and
that "B" is a dependency of "A."
part "A" relies on another part "B", you say that "A" depends on "B" and
that "B" is a dependency of "A".
You can ask a "dependency injection system" to create "A"
for us and handle all the dependencies.
If "A" needs "B" and "B" needs "C," the system resolves that chain of dependencies
and returns a fully prepared instance of "A."
and it will handle all of "A"s dependencies.
If "A" needs "B" and "B" needs "C", the system resolves that chain of dependencies
and returns a fully prepared instance of "A".
Angular provides and relies upon its own sophisticated
dependency-injection system
[dependency injection](!{docsLatest}/guide/dependency-injection.html) system
to assemble and run applications by "injecting" application parts
into other application parts where and when needed.
At the core, an [`injector`](#injector) returns dependency values on request.
At the core there is an [`injector`](#injector) that returns dependency values on request.
The expression `injector.get(token)` returns the value associated with the given token.
<!-- CF: Tokens are discussed in the next five paragraphs.
Should there be a separate glossary entry for "token" and this definition could link to it? -->
A token is an Angular type (`OpaqueToken`). You rarely need to work with tokens directly; most
A token is an Angular type (`OpaqueToken`). You rarely deal with tokens directly; most
methods accept a class name (`Foo`) or a string ("foo") and Angular converts it
to a token. When you write `injector.get(Foo)`, the injector returns
the value associated with the token for the `Foo` class, typically an instance of `Foo` itself.
@ -300,11 +292,9 @@ a#directives
.l-sub-section
:marked
An Angular class responsible for creating, reshaping, and interacting with HTML elements
in the browser DOM. The directive is Angular's most fundamental feature.
A directive is ususally associated with an HTML element or attribute.
This element or attribute is often referred to as the directive itself.
in the browser DOM. Directives are Angular's most fundamental feature.
A directive is almost always associated with an HTML element or attribute.
When Angular finds a directive in an HTML template,
it creates the matching directive class instance
and gives the instance control over that portion of the browser DOM.
@ -314,21 +304,19 @@ a#directives
as if you were writing native HTML. In this way, directives become extensions of
HTML itself.
Directives fall into three categories:
Directives fall into one of the following categories:
* [Components](#component) combine application logic with an HTML template to
1. [Components](#component) that combine application logic with an HTML template to
render application [views](#view). Components are usually represented as HTML elements.
They are the building blocks of an Angular application.
They are the building blocks of an Angular application and the
developer can expect to write a lot of them.
1. [Attribute directives](#attribute-directive) can listen to and modify the behavior of
other HTML elements, attributes, properties, and components. They are usually represented
1. [Attribute directives](#attribute-directive) that can listen to and modify the behavior of
HTML elements, components, and other directives. They are usually represented
as HTML attributes, hence the name.
1. [Structural directives](#structural-directive) are responsible for
shaping or reshaping HTML layout, typically by adding, removing, or manipulating
elements and their children.
1. [Structural directives](#structural-directive) that
shape or reshape HTML layout, typically by adding and removing elements in the DOM.
.l-main-section#E
@ -340,31 +328,32 @@ a#directives
The latest approved version of JavaScript is
[ECMAScript 2016](http://www.ecma-international.org/ecma-262/7.0/)
(also known as "ES2016" or "ES7"). Many Angular developers write their applications
in ES7 or a dialect that strives to be
(AKA "ES2016" or "ES7") and many Angular developers write their applications
either in this version of the language or a dialect that strives to be
compatible with it, such as [TypeScript](#typescript).
Most modern browsers only support the much older "ECMAScript 5" (also known as "ES5") standard.
Applications written in ES2016, ES2015, or one of their dialects must be [transpiled](#transpile)
Most modern browsers today only support the much older "ECMAScript 5" (AKA ES5) standard.
Applications written in ES2016, ES2015 or one of their dialects must be "[transpiled](#transpile)"
to ES5 JavaScript.
Angular developers can write in ES5 directly.
Angular developers may choose to write in ES5 directly.
:marked
## ES2015
.l-sub-section
:marked
Short hand for [ECMAScript](#ecmascript) 2015.
:marked
## ES6
.l-sub-section
:marked
Short hand for [ECMAScript](#ecmascript) 2015.
:marked
## ES5
.l-sub-section
:marked
Short hand for [ECMAScript](#ecmascript) 5, the version of JavaScript run by most modern browsers.
:marked
## ES6
.l-sub-section
:marked
Short hand for [ECMAScript](#ecmascript) 2015.
See [ECMAScript](#ecmascript).
a#F
a#G
@ -374,15 +363,15 @@ a#H
## Injector
.l-sub-section
:marked
An object in the Angular [dependency-injection system](#dependency-injection)
that can find a named dependency in its cache or create a dependency
An object in the Angular [dependency injection system](#dependency-injection)
that can find a named "dependency" in its cache or create such a thing
with a registered [provider](#provider).
:marked
## Input
.l-sub-section
:marked
A directive property that can be the *target* of a
A directive property that can be the ***target*** of a
[property binding](!{docsLatest}/guide/template-syntax.html#property-binding) (explained in detail in the [Template Syntax](!{docsLatest}/guide/template-syntax.html) page).
Data values flow *into* this property from the data source identified
in the template expression to the right of the equal sign.
@ -413,8 +402,8 @@ a#jit
## Just-in-time (JIT) compilation
.l-sub-section
:marked
A bootstrapping method of compiling components<span if-docs="ts"> and modules</span> in the browser
and launching the application dynamically. Just-in-time mode is a good choice during development.
With Angular _just-in-time_ bootstrapping you compile your components<span if-docs="ts"> and modules</span> in the browser
and launch the application dynamically. This is a good choice during development.
Consider using the [ahead-of-time](#aot) mode for production apps.
.l-main-section#K
@ -439,14 +428,14 @@ a#jit
For example, the `OnInit` interface has a hook method named `ngOnInit`.
Angular calls these hook methods in the following order:
* `ngOnChanges`: when an [input](#input)/[output](#output) binding value changes.
* `ngOnInit`: after the first `ngOnChanges`.
* `ngDoCheck`: developer's custom change detection.
* `ngAfterContentInit`: after component content initialized.
* `ngAfterContentChecked`: after every check of component content.
* `ngAfterViewInit`: after a component's views are initialized.
* `ngAfterViewChecked`: after every check of a component's views.
* `ngOnDestroy`: just before the directive is destroyed.
* `ngOnChanges` - when an [input](#input)/[output](#output) binding value changes.
* `ngOnInit` - after the first `ngOnChanges`.
* `ngDoCheck` - developer's custom change detection.
* `ngAfterContentInit` - after component content initialized.
* `ngAfterContentChecked` - after every check of component content.
* `ngAfterViewInit` - after component's view(s) are initialized.
* `ngAfterViewChecked` - after every check of a component's view(s).
* `ngOnDestroy` - just before the directive is destroyed.
Read more in the [Lifecycle Hooks](!{docsLatest}/guide/lifecycle-hooks.html) page.
@ -458,36 +447,35 @@ a#jit
block module-defn
.alert.is-important
:marked
Angular has the following types of modules:
In Angular, there are two types of modules:
- [Angular modules](#angular-module).
For details and examples, see the [Angular Modules](!{docsLatest}/guide/ngmodule.html) page.
- ES2015 modules, as described in this section.
:marked
A cohesive block of code dedicated to a single purpose.
Angular apps are modular.
In general, you assemble an application from many modules, both the ones you write and the ones you acquire from others.
In general, you assemble your application from many modules, both the ones you write and the ones you acquire from others.
A module *exports* something of value in that code, typically one thing such as a class.
A module that needs that thing *imports* it.
<!-- CF: Is there a more descriptive word than "thing" that can be used here? Perhaps "element"? -->
A typical module is a cohesive block of code dedicated to a single purpose.
A module **exports** something of value in that code, typically one thing such as a class.
A module that needs that thing, **imports** it.
The structure of Angular modules and the import/export syntax
is based on the [ES2015 module standard](http://www.2ality.com/2014/09/es6-modules-final.html).
An application that adheres to this standard requires a module loader to
load modules on request and resolve inter-module dependencies.
Angular doesn't include a module loader and doesn't have a preference
for any particular third-party library (although most examples use SystemJS).
You can use any module library that conforms to the standard.
load modules on request, and resolve inter-module dependencies.
Angular does not ship with a module loader and does not have a preference
for any particular 3rd party library (although most examples use SystemJS).
You may pick any module library that conforms to the standard.
Modules are typically named after the file in which the exported thing is defined.
The Angular [DatePipe](https://github.com/angular/angular/blob/master/modules/@angular/common/src/pipes/date_pipe.ts)
class belongs to a feature module named `date_pipe` in the file `date_pipe.ts`.
You rarely access Angular feature modules directly. You usually import them from an Angular [scoped package](#scoped-package) such as `@angular/core`.
You rarely access Angular feature modules directly. You usually import them from one of the Angular [scoped packages](#scoped-package) such as `@angular/core`.
a#N
.l-main-section#O
@ -497,7 +485,7 @@ a#N
## Observable
.l-sub-section
:marked
An array whose items arrive asynchronously over time.
You can think of an observable as an array whose items arrive asynchronously over time.
Observables help you manage asynchronous data, such as data coming from a backend service.
Observables are used within Angular itself, including Angular's event system and its http client service.
@ -508,9 +496,8 @@ a#N
## Output
.l-sub-section
:marked
A directive property that can be the *target* of event binding
(read more in the [event binding](!{docsLatest}/guide/template-syntax.html#event-binding)
section of the [Template Syntax](!{docsLatest}/guide/template-syntax.html) page).
A directive property that can be the ***target*** of
[event binding](!{docsLatest}/guide/template-syntax.html#event-binding).
Events stream *out* of this property to the receiver identified
in the template expression to the right of the equal sign.
@ -522,17 +509,14 @@ a#N
## PascalCase
.l-sub-section
:marked
The practice of writing individual words, compound words, or phrases such that each word or abbreviation begins with a capital letter.
Class names are typically spelled in PascalCase. For example, `Person` and `HeroDetailComponent`.
The practice of writing individual words, compound words, or phrases such that each word or abbreviation begins with a capital letter. Class names are typically spelled in PascalCase. Examples include: `Person` and `HeroDetailComponent`.
This form is also known as *upper camel case* to distinguish it from *lower camel case* or simply [camelCase](#camelcase).
In this documentation, "PascalCase" means *upper camel case* and "camelCase" means *lower camel case*.
This form is also known as **upper camel case** to distinguish it from **lower camel case**, which is simply called [camelCase](#camelcase). In this documentation, "PascalCase" means *upper camel case* and "camelCase" means *lower camel case*.
:marked
## Pipe
.l-sub-section
:marked
An Angular pipe is a function that transforms input values to output values for
display in a [view](#view).
Here's an example that uses the built-in `currency` pipe to display
@ -542,7 +526,6 @@ a#N
<label>Price: </label>{{product.price | currency}}
:marked
You can also write your own custom pipes.
Read more in the page on [pipes](!{docsLatest}/guide/pipes.html).
@ -563,15 +546,15 @@ a#Q
.l-sub-section
:marked
A technique for building Angular forms through code in a component.
The alternative technique is [template-driven forms](#template-driven-forms).
The alternate technique is [Template-Driven Forms](#template-driven-forms).
When building reactive forms:
- The "source of truth" is the component. The validation is defined using code in the component.
- Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
- The template input elements do *not* use `ngModel`.
- The associated Angular directives are all prefixed with `Form`, such as `FormGroup`, `FormControl`, and `FormControlName`.
- The associated Angular directives are all prefixed with `Form` such as `FormGroup`, `FormControl`, and `FormControlName`.
Reactive forms are powerful, flexible, and a good choice for more complex data-entry form scenarios, such as dynamic generation of form controls.
Reactive forms are powerful, flexible, and great for more complex data entry form scenarios such as dynamic generation of form controls.
:marked
## Router
@ -582,11 +565,11 @@ a#Q
and performing other similar actions that cause the application to
replace one view with another.
The Angular component router is a richly featured mechanism for configuring and managing the entire view navigation process, including the creation and destruction
The Angular [component router](!{docsLatest}/guide/router.html) is a richly featured mechanism for configuring and managing the entire view navigation process including the creation and destruction
of views.
+ifDocsFor('ts|js')
:marked
In most cases, components become attached to a router by means
In most cases, components become attached to a [router](#router) by means
of a `RouterConfig` that defines routes to views.
A [routing component's](#routing-component) template has a `RouterOutlet` element
@ -621,23 +604,31 @@ a#Q
## Scoped package
.l-sub-section
:marked
A way to group related *npm* packages.
Read more at the [npm-scope](https://docs.npmjs.com/misc/scope) page.
Angular modules are delivered within *scoped packages* such as `@angular/core`, `@angular/common`, `@angular/platform-browser-dynamic`,
`@angular/http`, and `@angular/router`.
Angular modules are delivered within *scoped packages* such as `@angular/core`,
`@angular/common`, `@angular/platform-browser-dynamic`, `@angular/http`, and `@angular/router`.
A [*scoped package*](https://docs.npmjs.com/misc/scope) is a way to group related *npm* packages.
Import a scoped package the same way that you import a normal package.
You import a scoped package the same way that you'd import a *normal* package.
The only difference, from a consumer perspective,
is that the scoped package name begins with the Angular *scope name*, `@angular`.
is that the *scoped package* name begins with the Angular *scope name*, `@angular`.
+makeExcerpt('architecture/ts/src/app/app.component.ts', 'import', '')
a#snake-case
:marked
## snake_case
.l-sub-section
block snake-case-defn
:marked
The practice of writing compound words or phrases such that an
underscore (`_`) separates one word from the next. This form is also known as **underscore case**.
:marked
## Service
.l-sub-section
:marked
For data or logic that is not associated
with a specific view or that you want to share across components, build services.
@ -648,23 +639,10 @@ a#Q
independent from any specific view,
provide shared data or logic across components, or encapsulate external interactions.
Applications often require services such as a data service or a logging service.
For more information, see the [Services](!{docsLatest}/tutorial/toh-pt4.html) page of the [Tour of Heroes](!{docsLatest}/tutorial/) tutorial.
a#snake-case
:marked
## snake_case
.l-sub-section
block snake-case-defn
:marked
The practice of writing compound words or phrases such that an
underscore (`_`) separates one word from the next. This form is also known as *underscore case*.
a#structural-directive
a#structural-directives
:marked
## Structural directives
.l-sub-section
@ -680,8 +658,8 @@ a#structural-directives
## Template
.l-sub-section
:marked
A chunk of HTML that Angular uses to render a [view](#view) with
the support and guidance of an Angular [directive](#directive),
A template is a chunk of HTML that Angular uses to render a [view](#view) with
the support and continuing guidance of an Angular [directive](#directive),
most notably a [component](#component).
@ -695,11 +673,11 @@ a#structural-directives
When building template-driven forms:
- The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
- [Two-way binding](#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
- [Two-way binding](#data-binding) with `ngModel` keeps the component model in synchronization with the user's entry into the input elements.
- Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
- The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
Template-driven forms are convenient, quick, and simple. They are a good choice for many basic data-entry form scenarios.
Template-driven forms are convenient, quick, and simple. They are a good choice for many basic data entry form scenarios.
Read about how to build template-driven forms
in the [Forms](!{docsLatest}/guide/forms.html) page.
@ -708,19 +686,18 @@ a#structural-directives
## Template expression
.l-sub-section
:marked
A !{_Lang}-like syntax that Angular evaluates within
An expression is a !{_Lang}-like syntax that Angular evaluates within
a [data binding](#data-binding).
Read about how to write template expressions
in the [Template expressions](!{docsLatest}/guide/template-syntax.html#template-expressions) section
of the [Template Syntax](!{docsLatest}/guide/template-syntax.html#) page.
in the [Template Syntax](!{docsLatest}/guide/template-syntax.html#template-expressions) page.
:marked
## Transpile
.l-sub-section
:marked
The process of transforming code written in one form of JavaScript
(such as TypeScript) into another form of JavaScript (such as [ES5](#es5)).
(for example, TypeScript) into another form of JavaScript (for example, [ES5](#es5)).
:marked
## TypeScript
@ -729,15 +706,15 @@ a#structural-directives
A version of JavaScript that supports most [ECMAScript 2015](#es2015)
language features such as [decorators](#decorator).
TypeScript is also notable for its optional typing system, which provides
compile-time type checking and strong tooling support (such as "intellisense,"
TypeScript is also noteable for its optional typing system, which enables
compile-time type checking and strong tooling support (for example, "intellisense",
code completion, refactoring, and intelligent search). Many code editors
and IDEs support TypeScript either natively or with plugins.
TypeScript is the preferred language for Angular development, although
TypeScript is the preferred language for Angular development although
you can use other JavaScript dialects such as [ES5](#es5).
Read more about TypeScript at [typescriptlang.org](http://www.typescriptlang.org/).
Read more about TypeScript at [typescript.org](http://www.typescriptlang.org/).
a#U
.l-main-section#V
@ -746,7 +723,7 @@ a#U
## View
.l-sub-section
:marked
A portion of the screen that displays information and responds
A view is a portion of the screen that displays information and responds
to user actions such as clicks, mouse moves, and keystrokes.
Angular renders a view under the control of one or more [directives](#directive),
@ -754,7 +731,7 @@ a#U
The component plays such a prominent role that it's often
convenient to refer to a component as a view.
Views often contain other views. Any view might be loaded and unloaded
Views often contain other views and any view might be loaded and unloaded
dynamically as the user navigates through the application, typically
under the control of a [router](#router).
@ -768,11 +745,11 @@ a#Y
.l-sub-section
block zone-defn
:marked
A mechanism for encapsulating and intercepting
Zones are a mechanism for encapsulating and intercepting
a JavaScript application's asynchronous activity.
The browser DOM and JavaScript have a limited number
of asynchronous activities, such as DOM events (for example, clicks),
of asynchronous activities, activities such as DOM events (for example, clicks),
[promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), and
[XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
calls to remote servers.
@ -781,7 +758,7 @@ a#Y
to take action before and after the async activity finishes.
Angular runs your application in a zone where it can respond to
asynchronous events by checking for data changes and updating
asynchronous events by checking for data changes, and updating
the information it displays via [data bindings](#data-binding).
Learn more about zones in this

View File

@ -1,83 +1,84 @@
include ../_util-fns
:marked
Weve all used a form to log in, 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.
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.
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.
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.
*That* takes design skills that are, to be frank, well out of scope for this guide.
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
It also takes framework support for
**two-way data binding, change tracking, validation, and error handling**
... which we shall cover in this guide on Angular forms.
- Build an Angular form with a component and template.
- Use `ngModel` to 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.
We will build a simple form from scratch, one step at a time. Along the way we'll learn how to:
You can run the <live-example></live-example> in Plunker and download the code from there.
- Build an Angular form with a component and template
- Use `ngModel` to 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
Run the <live-example></live-example>.
.l-main-section#template-driven
:marked
## Template-driven forms
You can build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
the form-specific directives and techniques described in this page.
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 guide.
.l-sub-section
:marked
You can also use a reactive (or model-driven) approach to build forms.
However, this page focuses on template-driven forms.
That's not the only way to create a form but it's the way we'll cover in this guide.
:marked
You can build almost any form with an Angular template&mdash;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,
We can build almost any form we need with an Angular template &mdash; login forms, contact forms, pretty much any business form.
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.
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd
otherwise wrestle with yourself.
It will be pretty easy because Angular handles many of the repetitive, boilerplate tasks we'd
otherwise wrestle with ourselves.
You'll learn to build a template-driven form that looks like this:
We'll discuss and learn to build a template-driven form that looks like this:
figure.image-display
img(src="/resources/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form")
:marked
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.
Here at the *Hero Employment Agency* we use this form to maintain personal information about heroes.
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 you delete the hero name, the form displays a validation error in an attention-grabbing style:
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/hero-form-2.png" width="400px" alt="Invalid, Name Required")
:marked
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
Note that the submit button is disabled, and the "required" bar to the left of the input control changed from green to red.
.l-sub-section
:marked
You can customize the colors and location of the "required" bar with standard CSS.
We'll customize the colors and location of the "required" bar with standard CSS.
:marked
You'll build this form in small steps:
We'll build this form in small steps:
1. Create the `Hero` model class.
1. Create the component that controls the form.
1. Create a template with the initial form layout.
1. Bind data properties to each form control using the `ngModel` two-way data-binding syntax.
1. Add a `name` attribute to each form-input control.
1. Bind data properties to each form control using the `ngModel` two-way data binding syntax.
1. Add a `name` attribute 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 *ngSubmit*.
1. Disable the forms *Submit* button until the form is valid.
1. Show and hide validation error messages.
1. Handle form submission with **ngSubmit**.
1. Disable the forms submit button until the form is valid.
:marked
## Setup
@ -87,11 +88,11 @@ figure.image-display
## Create the Hero model class
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.
As users enter form data, we'll capture their changes and update an instance of a model.
We can't lay out the form until we know what the model looks like.
A model can be as simple as a "property bag" that holds facts about a thing of application importance.
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
That describes well our `Hero` class with its three required fields (`id`, `name`, `power`)
and one optional field (`alterEgo`).
In the `!{_appDir}` directory, create the following file with the given content:
@ -99,14 +100,14 @@ figure.image-display
+makeExample('src/app/hero.ts')
:marked
It's an anemic model with few requirements and no behavior. Perfect for the demo.
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
automatically assigns the parameters value to that field when you create heroes.
assigns the parameters value to that field automatically when we create new heroes.
The `alterEgo` is optional, so the constructor lets you omit it; note the question mark (?) in `alterEgo?`.
The `alterEgo` is optional, so the constructor lets us omit it; note the (?) in `alterEgo?`.
You can create a new hero like this:
We can create a new hero like this:
+makeExcerpt('src/app/hero-form.component.ts', 'SkyDog', '')
@ -116,7 +117,7 @@ figure.image-display
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.
We begin with the class because it states, in brief, what the hero editor can do.
Create the following file with the given content:
@ -124,45 +125,45 @@ figure.image-display
:marked
Theres nothing special about this component, nothing form-specific,
nothing to distinguish it from any component you've written before.
nothing to distinguish it from any component we've written before.
Understanding this component requires only the Angular concepts covered in previous pages.
Understanding this component requires only the Angular concepts covered in previous guides.
- The code imports the Angular core library and the `Hero` model you just created.
- The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
- The `moduleId: module.id` property sets the base for module-relative loading of the `templateUrl`.
- The `templateUrl` property points to a separate file for the template HTML.
- You defined dummy data for `model` and `powers`, as befits a demo.
Down the road, you can inject a data service to get and save real data
or perhaps expose these properties as inputs and outputs
(see [Input and output properties](./template-syntax.html#inputs-outputs) on the
[Template Syntax](./template-syntax.html) page) for binding to a
parent component. This is not a concern now and these future changes won't affect the form.
- You added a `diagnostic` property to return a JSON representation of the model.
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
1. The code imports the Angular core library, and the `Hero` model we just created.
1. The `@Component` selector value of "hero-form" means we can drop this form in a parent template with a `<hero-form>` tag.
1. The `moduleId: module.id` property sets the base for module-relative loading of the `templateUrl`.
1. The `templateUrl` property points to a separate file for the template HTML.
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 threw in a `diagnostic` property 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.
### Why the separate template file?
Why don't you write the template inline in the component file as you often do elsewhere?
Why don't we write the template inline in the component file as we often do elsewhere?
There is no "right" answer for all occasions. Inline templates are useful when they are short.
Most form templates aren't short. TypeScript and JavaScript files generally aren't the best place to
write (or read) large stretches of HTML, and few editors help with files that have a mix of HTML and code.
There is no “right” answer for all occasions. We 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.
Form templates tend to be large, even when displaying a small number of fields,
Form templates tend to be quite large even when displaying a small number of fields
so it's usually best to put the HTML template in a separate file.
You'll write that template file in a moment. First,
revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
We'll write that template file in a moment. Before we do, we'll take a step back
and revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
.l-main-section
:marked
## Revise *app.module.ts*
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
and declare the components that belong to this module, such as the `HeroFormComponent`.
`app.module.ts` defines the application's root module. In it we identify the external modules we'll use in our application
and declare the components that belong to this module, such as our `HeroFormComponent`.
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
`imports` for the application module before you can use forms.
Because template-driven forms are in their own module, we need to add the `FormsModule` to the array of
`imports` for our application module before we can use forms.
Replace the contents of the "QuickStart" version with the following:
+makeExample('forms/ts/src/app/app.module.ts', null, 'src/app/app.module.ts')
@ -172,26 +173,24 @@ figure.image-display
:marked
There are three changes:
1. You import `FormsModule` and the new `HeroFormComponent`.
1. We import `FormsModule` and our new `HeroFormComponent`.
1. You add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives the application
1. We add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives our application
access to all of the template-driven forms features, including `ngModel`.
1. You add the `HeroFormComponent` to the list of `declarations` defined in the `ngModule` decorator. This makes
1. We add the `HeroFormComponent` to the list of `declarations` defined in the `ngModule` decorator. This makes
the `HeroFormComponent` component visible throughout this module.
.alert.is-important
:marked
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
If you wrote it and it should belong to this module, _do_ declare it in the `declarations` array.
// CF: Not sure I understand what "If you wrote it" means. Does it matter who wrote it?
What if someone else wrote the component but it should belong to this module? Should I declare it or not?
If a component, directive, or pipe belongs to a module in the `imports` array, _DON'T_ re-declare it in the `declarations` array.
If you wrote it and it should belong to this module, _DO_ declare it in the `declarations` array.
.l-main-section
:marked
## Revise *app.component.ts*
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
`AppComponent` is the application's root component. It will host our new `HeroFormComponent`.
Replace the contents of the "QuickStart" version with the following:
@ -202,40 +201,40 @@ figure.image-display
:marked
There are only two changes.
The `template` is simply the new element tag identified by the component's `selector` property.
This displays the hero form when the application component is loaded.
You've also dropped the `name` field from the class body.
This will display the hero form when the application component is loaded.
We've also dropped the `name` field from the class body.
.l-main-section
:marked
## Create an initial HTML form template
Create the template file with the following contents:
Create the new template file with the following contents:
+makeExample('src/app/hero-form.component.html', 'start')
:marked
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
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.
The *Name* `<input>` control has the HTML5 `required` attribute;
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.
We've got 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.
**We are not using Angular yet**. There are no bindings, no extra directives, just layout.
The `container`, `form-group`, `form-control`, and `btn` classes
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
Bootstrap gives the form a little style.
come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic.
We're using Bootstrap to give the form a little style!
.callout.is-important
header Angular forms don't require a style library
header Angular forms do not require a style library
:marked
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
the styles of any external library. Angular apps can use any CSS library or none at all.
the styles of any external library. Angular apps can use any CSS library, or none at all.
:marked
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
Let's add the stylesheet. Open `index.html` and add the following link to the `<head>`:
+makeExcerpt('src/index.html', 'bootstrap')
@ -243,12 +242,12 @@ figure.image-display
:marked
## Add powers with _*ngFor_
The hero must choose one superpower from a fixed list of agency-approved powers.
You maintain that list internally (in `HeroFormComponent`).
Our hero must choose one super power from a fixed list of Agency-approved powers.
We maintain that list internally (in `HeroFormComponent`).
You'll add a `select` to the
We'll add a `select` to our
form and bind the options to the `powers` list using `ngFor`,
a technique seen previously in the [Displaying Data](./displaying-data.html) page.
a technique seen previously in the [Displaying Data](./displaying-data.html) guide.
Add the following HTML *immediately below* the *Alter Ego* group:
@ -257,7 +256,7 @@ figure.image-display
:marked
This code repeats the `<option>` tag for each power in the list of powers.
The `pow` template input variable is a different power in each iteration;
you display its name using the interpolation syntax.
we display its name using the interpolation syntax.
.l-main-section#ngModel
:marked
@ -268,17 +267,17 @@ figure.image-display
figure.image-display
img(src="/resources/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding")
:marked
You don't see hero data because you're not binding to the `Hero` yet.
You know how to do that from earlier pages.
[Displaying Data](./displaying-data.html) teaches property binding.
[User Input](./user-input.html) shows how to listen for DOM events with an
We don't see hero data because we are not binding to the `Hero` yet.
We know how to do that from earlier guides.
[Displaying Data](./displaying-data.html) taught us property binding.
[User Input](./user-input.html) showed us 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.
Now we 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.
We could use the techniques we already know, but
instead we'll introduce something new: the `[(ngModel)]` syntax, which
makes binding the form to the model super easy.
Find the `<input>` tag for *Name* and update it like this:
@ -286,17 +285,17 @@ figure.image-display
.l-sub-section
:marked
You added a diagnostic interpolation after the input tag
so you can see what you're doing.
You left yourself a note to throw it away when you're done.
We added a diagnostic interpolation after the input tag
so we can see what we're doing.
We left ourselves a note to throw it away when we're done.
:marked
Focus on the binding syntax: `[(ngModel)]="..."`.
If you ran the app now and started typing in the *Name* input box,
adding and deleting characters, you'd see them appear and disappear
If we run 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:
At some point it might look like this.
figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action")
@ -307,30 +306,29 @@ figure.image-display
.l-sub-section
:marked
That's *two-way data binding*.
For more information, see
[Two-way binding with NgModel](template-syntax.html#ngModel) on the
the [Template Syntax](template-syntax.html) page.
That's **two-way data binding**!
For more information about `[(ngModel)]` and two-way data bindings, see
the [Template Syntax](template-syntax.html#ngModel) page.
:marked
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
Notice that we also added a `name` attribute to our `<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.
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
.l-sub-section
:marked
Internally, Angular creates `FormControl` instances and
Internally Angular creates `FormControl` instances and
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
Each `FormControl` is registered under the name you assigned to the `name` attribute.
Read more in [The NgForm directive](#ngForm), later in this page.
Each `FormControl` is registered under the name we assigned to the `name` attribute.
We'll talk about `NgForm` [later in this guide](#ngForm).
:marked
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
You'll ditch the input box binding message
Let's add similar `[(ngModel)]` bindings and `name` attributes 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 you can confirm that two-way data binding works *for the entire hero model*.
Then we can confirm that two-way data binding works *for the entire hero model*.
After revision, the core of the form should look like this:
After revision, the core of our form should look like this:
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'ngModel-2')
@ -341,26 +339,28 @@ figure.image-display
- Each input element has a `name` property that is required by Angular forms to register the control with the form.
:marked
If you run the app now and change every hero model property, the form might display like this:
If we run the app now and changed every hero model property, the form might display like this:
figure.image-display
img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action")
:marked
The diagnostic near the top of the form
confirms that all of your changes are reflected in the model.
confirms that all of our changes are reflected in the model.
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
**Delete** the `{{diagnostic}}` binding at the top as it has served its purpose.
.l-main-section
:marked
## Track control state and validity with _ngModel_
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
you if the user touched the control, if the value changed, or if the value became invalid.
A form isn't just about data binding. We'd also like to know the state of the controls in our form.
Using `ngModel` in a form gives us more than just a two way data binding. It also tells
us if the user touched the control, if the value changed, or if the value became invalid.
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
You can leverage those class names to change the appearance of the control.
We can leverage those class names to change the appearance of the control.
table
tr
@ -368,27 +368,27 @@ table
th Class if true
th Class if false
tr
td The control has been visited.
td Control has been visited
td <code>ng-touched</code>
td <code>ng-untouched</code>
tr
td The control's value has changed.
td Control's value has changed
td <code>ng-dirty</code>
td <code>ng-pristine</code>
tr
td The control's value is valid.
td Control's value is valid
td <code>ng-valid</code>
td <code>ng-invalid</code>
:marked
Temporarily add a [template reference variable](./template-syntax.html#ref-vars) named `spy`
Let's temporarily add a [template reference variable](./template-syntax.html#ref-vars) named `spy`
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'ngModelName-2')
:marked
Now run the app and look at the _Name_ input box.
Follow these steps *precisely*:
Now run the app, and look at the _Name_ input box.
Follow the next four steps *precisely*:
1. Look but don't touch.
1. Click inside the name box, then click outside it.
@ -401,31 +401,31 @@ figure.image-display
img(src="/resources/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition")
:marked
You should see the following transitions and class names:
We should see the following transitions and class names:
figure.image-display
img(src="/resources/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions")
:marked
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
strong visual signal when the values are invalid. You also want to mark required fields.
To create such visual feedback, add definitions for the `ng-*` CSS classes.
The `ng-valid`/`ng-invalid` pair is the most interesting to us, because we want to send a
strong visual signal when the values are invalid. We also want to mark required fields.
To create such visual feedback, let's add definitions for the `ng-*` CSS classes.
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
**Delete** the `#spy` template reference variable and the `TODO` as they have served their purpose.
.l-main-section
:marked
## Add custom CSS for visual feedback
You can mark required fields and invalid data at the same time with a colored bar
We can mark required fields and invalid data at the same time with a colored bar
on the left of the input box:
figure.image-display
img(src="/resources/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form")
:marked
You achieve this effect by adding these class definitions to a new `forms.css` file
that you add to the project as a sibling to `index.html`:
We achieve this effect by adding these class definitions to a new `forms.css` file
that we add to our project as a sibling to `index.html`:
+makeExample('src/forms.css')
@ -437,63 +437,64 @@ figure.image-display
:marked
## Show and hide validation error messages
You can improve the form. The _Name_ input box is required and clearing it turns the bar 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.
We can do better. The _Name_ input box is required and clearing it turns the bar red.
That says *something* is wrong but we don't know *what* is wrong or what to do about it.
We can leverage the control's state to reveal a helpful message.
When the user deletes the name, the form should look like this:
Here's the way it should look when the user deletes the name:
figure.image-display
img(src="/resources/images/devguide/forms/name-required-error.png" width="400px" alt="Name required")
:marked
To achieve this effect, extend the `<input>` tag with the following:
- A [template reference variable](./template-syntax.html#ref-vars).
- The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
To achieve this effect we extend the `<input>` tag with
1. a [template reference variable](./template-syntax.html#ref-vars)
1. the "*is required*" message in a nearby `<div>` which we'll display only if the control is invalid.
Here's an example of an error message added to the _name_ input box:
Here's an example of adding an error message to the _name_ input box:
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'name-with-error-msg')
:marked
You need a template reference variable to access the input box's Angular control from within the template.
Here you created a variable called `name` and gave it the value "ngModel".
We need a template reference variable to access the input box's Angular control from within the template.
Here we created a variable called `name` and gave it the value "ngModel".
.l-sub-section
:marked
Why "ngModel"?
A directive's [exportAs](../api/core/index/Directive-decorator.html) property
tells Angular how to link the reference variable to the directive.
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
We set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
:marked
You control visibility of the name error message by binding properties of the `name`
We control visibility of the name error message by binding properties of the `name`
control to the message `<div>` element's `hidden` property.
+makeExcerpt('src/app/hero-form.component.html', 'hidden-error-msg', '')
:marked
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.
In this example, we 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.
This 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.
This user experience is the developer's choice. Some folks want to see the message at all times.
If we ignore the `pristine` state, we would hide the message only when the value is valid.
If we arrive in this component with a new (blank) hero or an invalid hero,
we'll see the error message immediately, before we've done anything.
Some developers want to the message to display only when the user makes an invalid change.
Some folks find that behavior disconcerting.
They only want to see the message 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 new hero](#new-hero) to the form.
We'll see the significance of this choice when we [add a new hero](#new-hero) to the form.
The hero *Alter Ego* is optional so you can leave that be.
The hero *Alter Ego* is optional so we can leave that be.
Hero *Power* selection is required.
You can add the same kind of error handling to the `<select>` if you want,
We can add the same kind of error handling to the `<select>` if we want,
but it's not imperative because the selection box already constrains the
power to valid values.
Now you'll add a new hero in this form.
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
We'd like to add a new hero in this form.
We place a "New Hero" button at the bottom of the form and bind its click event to a `newHero` component method.
+makeExample('forms/ts/src/app/hero-form.component.html',
'new-hero-button-no-reset',
@ -507,18 +508,18 @@ figure.image-display
Run the application again, click the *New Hero* button, and the form clears.
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
That's understandable as these are required fields.
The error messages are hidden because the form is pristine; you haven't changed anything yet.
The error messages are hidden because the form is pristine; we haven't changed anything yet.
Enter a name and click *New Hero* again.
The app displays a _Name is required_ error message.
You don't want error messages when you create a new (empty) hero.
Why are you getting one now?
The app displays a **_Name is required_** error message!
We don't want error messages when we create a new (empty) hero.
Why are we getting one now?
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
The form remembers that you entered a name before clicking *New Hero*.
The form remembers that we entered a name before clicking *New Hero*.
Replacing the hero object *did not restore the pristine state* of the form controls.
You have to clear all of the flags imperatively, which you can do
We have to clear all of the flags imperatively which we can do
by calling the form's `reset()` method after calling the `newHero()` method.
+makeExample('forms/ts/src/app/hero-form.component.html',
@ -526,14 +527,14 @@ figure.image-display
'src/app/hero-form.component.html (Reset the form)')
:marked
Now clicking "New Hero" resets both the form and its control flags.
Now clicking "New Hero" both resets the form and its control flags.
.l-main-section
:marked
## 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
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 (`type="submit"`).
@ -544,8 +545,8 @@ figure.image-display
+makeExcerpt('forms/ts/src/app/hero-form.component.html (ngSubmit)')
:marked
You added something extra at the end. You defined a
template reference variable, `#heroForm`, and initialized it with the value "ngForm".
We slipped in something extra there at the end! We defined a
template reference variable, **`#heroForm`**, and initialized it with the value "ngForm".
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
@ -554,35 +555,35 @@ figure.image-display
### The _NgForm_ directive
What `NgForm` directive?
You didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive.
We didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive!
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
The `NgForm` directive supplements the `form` element with additional features.
It holds the controls you created for the elements with an `ngModel` directive
and `name` attribute, and monitors their properties, including their validity.
It holds the controls we created for the elements with an `ngModel` directive
and `name` attribute, and monitors their properties including their validity.
It also has its own `valid` property which is true only *if every contained
control* is valid.
:marked
You'll bind the form's overall validity via
We'll bind the form's overall validity via
the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code:
+makeExcerpt('src/app/hero-form.component.html', 'submit-button', '')
:marked
If you run the application now, you find that the button is enabled&mdash;although
it doesn't do anything useful yet.
If we run the application now, we find that the button is enabled
&mdash; although it doesn't do anything useful yet.
Now if you delete the Name, you violate the "required" rule, which
Now if we delete the Name, we violate the "required" rule, which
is duly noted in the error message.
The *Submit* button is also disabled.
The Submit button is also disabled.
Not impressed? Think about it for a moment. What would you have to do to
Not impressed? Think about it for a moment. What would we 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:
For us, it was as simple as:
1. Define a template reference variable on the (enhanced) form element.
2. Refer to that variable in a button many lines away.
@ -596,63 +597,65 @@ figure.image-display
.l-sub-section
:marked
An unsurprising observation for a demo. To be honest,
jazzing it up won't teach you anything new about forms.
But this is an opportunity to exercise some of your newly won
jazzing it up won't teach us anything new about forms.
But this is an opportunity to exercise some of our newly won
binding skills.
If you aren't interested, skip to this page's conclusion.
If you aren't interested, go ahead and skip to this guide's conclusion.
:marked
For a more strikingly visual effect,
hide the data entry area and display something else.
Let's do something more strikingly visual.
Let's hide the data entry area and display something else.
Wrap the form in a `<div>` and bind
Start by wrapping the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property.
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'edit-div')
:marked
The main form is visible from the start because the
`submitted` property is false until you submit the form,
`submitted` property is false until we submit the form,
as this fragment from the `HeroFormComponent` shows:
+makeExcerpt('src/app/hero-form.component.ts', 'submitted')
:marked
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
When we click the Submit button, the `submitted` flag becomes true and the form disappears
as planned.
Now the app needs to show something else while the form is in the submitted state.
Add the following HTML below the `<div>` wrapper you just wrote:
Add the following HTML below the `<div>` wrapper we just wrote:
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'submitted')
:marked
There's the hero again, displayed read-only with interpolation bindings.
There's our hero again, displayed read-only with interpolation bindings.
This `<div>` appears only while the component is in the submitted state.
The HTML includes an *Edit* button whose click event is bound to an expression
The HTML includes an _Edit_ button whose click event is bound to an expression
that clears the `submitted` flag.
When you click the *Edit* button, this block disappears and the editable form reappears.
When we click the _Edit_ button, this block disappears and the editable form reappears.
That's as much drama as we can muster for now.
.l-main-section
:marked
## Conclusion
The Angular form discussed in this page takes advantage of the following
The Angular form discussed in this guide takes advantage of the following
framework features to provide support for data modification, validation, and more:
- An Angular HTML form template.
- A form component class with a `@Component` decorator.
- Handling form submission by binding to the `NgForm.ngSubmit` event property.
- Template-reference variables such as `#heroForm` and `#name`.
- Template reference variables such as `#heroForm` and `#name`.
- `[(ngModel)]` syntax for two-way data binding.
- The use of `name` attributes for validation and form-element change tracking.
- The use of `name` attributes for validation and form element change tracking.
- The reference variables `valid` property on input controls to check if a control is valid and show/hide error messages.
- Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
- Controlling the submit button's enabled state by binding to `NgForm` validity.
- Custom CSS classes that provide visual feedback to users about invalid controls.
The final project folder structure should look like this:
Our final project folder structure should look like this:
.filetree
.file angular-forms