docs(Guide): Content edits in the Angular 2 Guide. (#3159)
* Content edits in the Angular 2 "Forms" page. * Updates based on @juleskremer's feedback. * Added bold formatting for UI button names. * [WIP] Copyedits to the Glossary. Includes several questions and comments--search for "CF:" * Updated with Jules's feedback and fixed a few issues that I didn't catch the first time.
This commit is contained in:
parent
b874d4874f
commit
01497865f8
|
@ -9,13 +9,14 @@ block includes
|
|||
- var _decoratorLink = '<a href="#' + _decorator + '">' + _decorator + '</a>'
|
||||
|
||||
:marked
|
||||
Angular has a vocabulary of its own.
|
||||
Most Angular terms are everyday English words
|
||||
Angular has its own vocabulary.
|
||||
Most Angular terms are common 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)
|
||||
|
@ -28,7 +29,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.
|
||||
|
@ -41,10 +42,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 Module](!{docsLatest}/guide/ngmodule.html) page.
|
||||
For details and examples, see the [Angular Modules (NgModule)](!{docsLatest}/guide/ngmodule.html) page.
|
||||
|
||||
+ifDocsFor('ts|dart')
|
||||
:marked
|
||||
|
@ -64,7 +65,7 @@ a#attribute-directives
|
|||
other HTML elements, attributes, properties, and components. They are usually represented
|
||||
as HTML attributes, hence the name.
|
||||
|
||||
A good example of an attribute directive is the `ngClass` directive for adding and removing CSS class names.
|
||||
For example, you can use the `ngClass` directive to add and remove CSS class names.
|
||||
|
||||
Learn about them in the [_Attribute Directives_](!{docsLatest}/guide/attribute-directives.html) guide.
|
||||
|
||||
|
@ -75,10 +76,10 @@ a#attribute-directives
|
|||
## Barrel
|
||||
.l-sub-section
|
||||
:marked
|
||||
A barrel is a way to *rollup exports* from several ES2015 modules into a single convenient ES2015 module.
|
||||
A way to *roll up 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.
|
||||
|
||||
Imagine three ES2015 modules in a `heroes` folder:
|
||||
For example, imagine three ES2015 modules in a `heroes` folder:
|
||||
code-example.
|
||||
// heroes/hero.component.ts
|
||||
export class HeroComponent {}
|
||||
|
@ -89,7 +90,7 @@ a#attribute-directives
|
|||
// heroes/hero.service.ts
|
||||
export class HeroService {}
|
||||
:marked
|
||||
Without a barrel, a consumer would need three import statements:
|
||||
Without a barrel, a consumer needs three import statements:
|
||||
code-example.
|
||||
import { HeroComponent } from '../heroes/hero.component.ts';
|
||||
import { Hero } from '../heroes/hero.model.ts';
|
||||
|
@ -109,18 +110,18 @@ a#attribute-directives
|
|||
|
||||
.alert.is-important
|
||||
:marked
|
||||
Note that you can often achieve this using [Angular modules](#angular-module) instead.
|
||||
You can often achieve the same result using [Angular modules](#angular-module) instead.
|
||||
|
||||
:marked
|
||||
## Binding
|
||||
.l-sub-section
|
||||
:marked
|
||||
Almost always refers to [Data Binding](#data-binding) and the act of
|
||||
Usually refers to [data binding](#data-binding) and the act of
|
||||
binding an HTML object property to a data object property.
|
||||
|
||||
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.
|
||||
Sometimes refers to a [dependency-injection](#dependency-injection) binding
|
||||
between a "token"—also referred to as a "key"—and a dependency [provider](#provider).
|
||||
When using this more rare usage, be clear in context.
|
||||
|
||||
:marked
|
||||
## Bootstrap
|
||||
|
@ -130,7 +131,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 with its own top level root.
|
||||
You can bootstrap multiple apps in the same `index.html`, each app with its own top-level root.
|
||||
|
||||
.l-main-section#C
|
||||
:marked
|
||||
|
@ -140,10 +141,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. Examples include: `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. For example, `square`, `firstName`, and `getHeroes`. Notice that `square` is an example of how you write a single word in camelCase.
|
||||
|
||||
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*.
|
||||
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*.
|
||||
|
||||
a#component
|
||||
:marked
|
||||
|
@ -155,9 +156,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).
|
||||
|
||||
You apply the `!{_at}Component` !{_decoratorLink} to
|
||||
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 it with its template
|
||||
that Angular needs to create a component instance and render the component with its template
|
||||
as a view.
|
||||
|
||||
Those familiar with "MVC" and "MVVM" patterns will recognize
|
||||
|
@ -169,7 +170,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](#kebab-case).
|
||||
This form is also known as 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
|
||||
|
@ -180,17 +181,18 @@ a#component
|
|||
.l-sub-section
|
||||
:marked
|
||||
Applications display data values to a user and respond to user
|
||||
actions (clicks, touches, keystrokes).
|
||||
|
||||
Instead of manually pushing application data values into HTML, attaching
|
||||
actions (such as clicks, touches, and 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
|
||||
event listeners, pulling changed values from the screen, and
|
||||
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.
|
||||
updating application data values.
|
||||
|
||||
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 forms of binding in the [Template Syntax](!{docsLatest}/guide/template-syntax.html) page:
|
||||
Read about the following 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).
|
||||
|
@ -208,14 +210,14 @@ a#component
|
|||
.l-sub-section
|
||||
block decorator-defn
|
||||
:marked
|
||||
A decorator is a **function** that adds metadata to a class, its members (properties, methods) and function arguments.
|
||||
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 (AKA ES7).
|
||||
Decorators are a JavaScript language [feature](https://github.com/wycats/javascript-decorators), implemented in TypeScript and proposed for ES2016 (also known as ES7).
|
||||
|
||||
To apply a decorator, position it immediately above or to the left of the thing it decorates.
|
||||
To apply a decorator, position it immediately above or to the left of the item it decorates.
|
||||
|
||||
Angular has its own set of decorators to help it interoperate with your application parts.
|
||||
Here is an example of a `@Component` decorator that identifies a
|
||||
The following example is 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.
|
||||
```
|
||||
|
@ -227,7 +229,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 appearing below it in the file.
|
||||
classes that follow it in the file.
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
|
@ -237,7 +239,10 @@ a#component
|
|||
## Dependency injection
|
||||
.l-sub-section
|
||||
:marked
|
||||
Dependency injection is both a design pattern and a mechanism
|
||||
<!-- 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
|
||||
for creating and delivering parts of an application to other
|
||||
parts of an application that request them.
|
||||
|
||||
|
@ -246,23 +251,26 @@ 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"
|
||||
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".
|
||||
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."
|
||||
|
||||
|
||||
Angular provides and relies upon its own sophisticated
|
||||
[dependency injection](!{docsLatest}/guide/dependency-injection.html) system
|
||||
dependency-injection system
|
||||
to assemble and run applications by "injecting" application parts
|
||||
into other application parts where and when needed.
|
||||
|
||||
At the core there is an [`injector`](#injector) that returns dependency values on request.
|
||||
At the core, an [`injector`](#injector) 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 deal with tokens directly; most
|
||||
A token is an Angular type (`OpaqueToken`). You rarely need to work 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.
|
||||
|
@ -292,9 +300,11 @@ a#directives
|
|||
.l-sub-section
|
||||
:marked
|
||||
An Angular class responsible for creating, reshaping, and interacting with HTML elements
|
||||
in the browser DOM. Directives are Angular's most fundamental feature.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
@ -304,19 +314,21 @@ a#directives
|
|||
as if you were writing native HTML. In this way, directives become extensions of
|
||||
HTML itself.
|
||||
|
||||
Directives fall into three categories:
|
||||
|
||||
1. [Components](#component) that combine application logic with an HTML template to
|
||||
Directives fall into one of the following categories:
|
||||
|
||||
* [Components](#component) 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 and the
|
||||
developer can expect to write a lot of them.
|
||||
They are the building blocks of an Angular application.
|
||||
|
||||
1. [Attribute directives](#attribute-directive) that can listen to and modify the behavior of
|
||||
HTML elements, components, and other directives. They are usually represented
|
||||
1. [Attribute directives](#attribute-directive) can listen to and modify the behavior of
|
||||
other HTML elements, attributes, properties, and components. They are usually represented
|
||||
as HTML attributes, hence the name.
|
||||
|
||||
1. [Structural directives](#structural-directive) that
|
||||
shape or reshape HTML layout, typically by adding and removing elements in the DOM.
|
||||
1. [Structural directives](#structural-directive) are responsible for
|
||||
shaping or reshaping HTML layout, typically by adding, removing, or manipulating
|
||||
elements and their children.
|
||||
|
||||
|
||||
.l-main-section#E
|
||||
|
||||
|
@ -328,32 +340,31 @@ a#directives
|
|||
|
||||
The latest approved version of JavaScript is
|
||||
[ECMAScript 2016](http://www.ecma-international.org/ecma-262/7.0/)
|
||||
(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
|
||||
(also known as "ES2016" or "ES7"). Many Angular developers write their applications
|
||||
in ES7 or a dialect that strives to be
|
||||
compatible with it, such as [TypeScript](#typescript).
|
||||
|
||||
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)"
|
||||
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)
|
||||
to ES5 JavaScript.
|
||||
|
||||
Angular developers may choose to write in ES5 directly.
|
||||
Angular developers can 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.
|
||||
See [ECMAScript](#ecmascript).
|
||||
:marked
|
||||
## ES6
|
||||
.l-sub-section
|
||||
:marked
|
||||
Short hand for [ECMAScript](#ecmascript) 2015.
|
||||
|
||||
a#F
|
||||
a#G
|
||||
|
@ -363,15 +374,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 such a thing
|
||||
An object in the Angular [dependency-injection system](#dependency-injection)
|
||||
that can find a named dependency in its cache or create a dependency
|
||||
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.
|
||||
|
@ -402,8 +413,8 @@ a#jit
|
|||
## Just-in-time (JIT) compilation
|
||||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
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.
|
||||
Consider using the [ahead-of-time](#aot) mode for production apps.
|
||||
|
||||
.l-main-section#K
|
||||
|
@ -428,14 +439,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 component's view(s) are initialized.
|
||||
* `ngAfterViewChecked` - after every check of a component's view(s).
|
||||
* `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 a component's views are initialized.
|
||||
* `ngAfterViewChecked`: after every check of a component's views.
|
||||
* `ngOnDestroy`: just before the directive is destroyed.
|
||||
|
||||
Read more in the [Lifecycle Hooks](!{docsLatest}/guide/lifecycle-hooks.html) page.
|
||||
|
||||
|
@ -447,35 +458,36 @@ a#jit
|
|||
block module-defn
|
||||
.alert.is-important
|
||||
:marked
|
||||
In Angular, there are two types of modules:
|
||||
Angular has the following 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 your application from many modules, both the ones you write and the ones you acquire from others.
|
||||
|
||||
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.
|
||||
In general, you assemble an 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"? -->
|
||||
|
||||
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 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.
|
||||
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.
|
||||
|
||||
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 one of the Angular [scoped packages](#scoped-package) such as `@angular/core`.
|
||||
You rarely access Angular feature modules directly. You usually import them from an Angular [scoped package](#scoped-package) such as `@angular/core`.
|
||||
|
||||
a#N
|
||||
.l-main-section#O
|
||||
|
@ -485,19 +497,20 @@ a#N
|
|||
## Observable
|
||||
.l-sub-section
|
||||
:marked
|
||||
You can think of an observable as an array whose items arrive asynchronously over time.
|
||||
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.
|
||||
|
||||
To use observables, Angular uses a third-party library called Reactive Extensions (RxJS).
|
||||
Observables are a proposed feature for ES 2016, the next version of JavaScript.
|
||||
Observables are a proposed feature for ES2016, the next version of JavaScript.
|
||||
|
||||
:marked
|
||||
## Output
|
||||
.l-sub-section
|
||||
:marked
|
||||
A directive property that can be the ***target*** of
|
||||
[event binding](!{docsLatest}/guide/template-syntax.html#event-binding).
|
||||
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).
|
||||
Events stream *out* of this property to the receiver identified
|
||||
in the template expression to the right of the equal sign.
|
||||
|
||||
|
@ -509,14 +522,17 @@ 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. Examples include: `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. For example, `Person` and `HeroDetailComponent`.
|
||||
|
||||
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*.
|
||||
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*.
|
||||
|
||||
: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
|
||||
|
@ -526,6 +542,7 @@ 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).
|
||||
|
||||
|
@ -546,15 +563,15 @@ a#Q
|
|||
.l-sub-section
|
||||
:marked
|
||||
A technique for building Angular forms through code in a component.
|
||||
The alternate technique is [Template-Driven Forms](#template-driven-forms).
|
||||
The alternative 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 great for more complex data entry form scenarios such as dynamic generation of form controls.
|
||||
Reactive forms are powerful, flexible, and a good choice for more complex data-entry form scenarios, such as dynamic generation of form controls.
|
||||
|
||||
:marked
|
||||
## Router
|
||||
|
@ -565,11 +582,11 @@ a#Q
|
|||
and performing other similar actions that cause the application to
|
||||
replace one view with another.
|
||||
|
||||
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
|
||||
The Angular component router 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](#router) by means
|
||||
In most cases, components become attached to a router by means
|
||||
of a `RouterConfig` that defines routes to views.
|
||||
|
||||
A [routing component's](#routing-component) template has a `RouterOutlet` element
|
||||
|
@ -604,31 +621,23 @@ a#Q
|
|||
## Scoped package
|
||||
.l-sub-section
|
||||
:marked
|
||||
Angular modules are delivered within *scoped packages* such as `@angular/core`, `@angular/common`, `@angular/platform-browser-dynamic`,
|
||||
`@angular/http`, and `@angular/router`.
|
||||
A way to group related *npm* packages.
|
||||
Read more at the [npm-scope](https://docs.npmjs.com/misc/scope) page.
|
||||
|
||||
A [*scoped package*](https://docs.npmjs.com/misc/scope) is a way to group related *npm* packages.
|
||||
Angular modules are delivered within *scoped packages* such as `@angular/core`,
|
||||
`@angular/common`, `@angular/platform-browser-dynamic`, `@angular/http`, and `@angular/router`.
|
||||
|
||||
You import a scoped package the same way that you'd import a *normal* package.
|
||||
Import a scoped package the same way that you 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.
|
||||
|
||||
|
@ -639,10 +648,23 @@ a#snake-case
|
|||
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
|
||||
|
@ -658,8 +680,8 @@ a#structural-directives
|
|||
## Template
|
||||
.l-sub-section
|
||||
:marked
|
||||
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),
|
||||
A chunk of HTML that Angular uses to render a [view](#view) with
|
||||
the support and guidance of an Angular [directive](#directive),
|
||||
most notably a [component](#component).
|
||||
|
||||
|
||||
|
@ -673,11 +695,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 in synchronization with the user's entry into the input elements.
|
||||
- [Two-way binding](#data-binding) with `ngModel` keeps the component model synchronized 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.
|
||||
|
@ -686,18 +708,19 @@ a#structural-directives
|
|||
## Template expression
|
||||
.l-sub-section
|
||||
:marked
|
||||
An expression is a !{_Lang}-like syntax that Angular evaluates within
|
||||
A !{_Lang}-like syntax that Angular evaluates within
|
||||
a [data binding](#data-binding).
|
||||
|
||||
Read about how to write template expressions
|
||||
in the [Template Syntax](!{docsLatest}/guide/template-syntax.html#template-expressions) page.
|
||||
in the [Template expressions](!{docsLatest}/guide/template-syntax.html#template-expressions) section
|
||||
of the [Template Syntax](!{docsLatest}/guide/template-syntax.html#) page.
|
||||
|
||||
:marked
|
||||
## Transpile
|
||||
.l-sub-section
|
||||
:marked
|
||||
The process of transforming code written in one form of JavaScript
|
||||
(for example, TypeScript) into another form of JavaScript (for example, [ES5](#es5)).
|
||||
(such as TypeScript) into another form of JavaScript (such as [ES5](#es5)).
|
||||
|
||||
:marked
|
||||
## TypeScript
|
||||
|
@ -706,15 +729,15 @@ a#structural-directives
|
|||
A version of JavaScript that supports most [ECMAScript 2015](#es2015)
|
||||
language features such as [decorators](#decorator).
|
||||
|
||||
TypeScript is also noteable for its optional typing system, which enables
|
||||
compile-time type checking and strong tooling support (for example, "intellisense",
|
||||
TypeScript is also notable for its optional typing system, which provides
|
||||
compile-time type checking and strong tooling support (such as "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 [typescript.org](http://www.typescriptlang.org/).
|
||||
Read more about TypeScript at [typescriptlang.org](http://www.typescriptlang.org/).
|
||||
|
||||
a#U
|
||||
.l-main-section#V
|
||||
|
@ -723,7 +746,7 @@ a#U
|
|||
## View
|
||||
.l-sub-section
|
||||
:marked
|
||||
A view is a portion of the screen that displays information and responds
|
||||
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),
|
||||
|
@ -731,7 +754,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 and any view might be loaded and unloaded
|
||||
Views often contain other views. Any view might be loaded and unloaded
|
||||
dynamically as the user navigates through the application, typically
|
||||
under the control of a [router](#router).
|
||||
|
||||
|
@ -745,11 +768,11 @@ a#Y
|
|||
.l-sub-section
|
||||
block zone-defn
|
||||
:marked
|
||||
Zones are a mechanism for encapsulating and intercepting
|
||||
A mechanism for encapsulating and intercepting
|
||||
a JavaScript application's asynchronous activity.
|
||||
|
||||
The browser DOM and JavaScript have a limited number
|
||||
of asynchronous activities, activities such as DOM events (for example, clicks),
|
||||
of asynchronous 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.
|
||||
|
@ -758,7 +781,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
|
||||
|
|
|
@ -1,84 +1,83 @@
|
|||
include ../_util-fns
|
||||
|
||||
:marked
|
||||
We’ve 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.
|
||||
|
||||
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.
|
||||
In developing a form, it's important to create a data-entry experience that guides the
|
||||
user efficiently and effectively through the workflow.
|
||||
|
||||
*That* takes design skills that are, to be frank, well out of scope for this guide.
|
||||
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.
|
||||
|
||||
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.
|
||||
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
- 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>.
|
||||
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||
|
||||
.l-main-section#template-driven
|
||||
:marked
|
||||
## Template-driven forms
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
That's not the only way to create a form but it's the way we'll cover in this guide.
|
||||
You can also use a reactive (or model-driven) approach to build forms.
|
||||
However, this page focuses on template-driven forms.
|
||||
|
||||
:marked
|
||||
We can build almost any form we need with an Angular template — 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,
|
||||
You can build almost any form with an Angular template—login forms, contact forms, and pretty much any business form.
|
||||
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
|
||||
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
|
||||
|
||||
It will be pretty easy because Angular handles many of the repetitive, boilerplate tasks we'd
|
||||
otherwise wrestle with ourselves.
|
||||
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd
|
||||
otherwise wrestle with yourself.
|
||||
|
||||
We'll discuss and learn to build a template-driven form that looks like this:
|
||||
You'll 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
|
||||
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!
|
||||
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
|
||||
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
|
||||
|
||||
Two of the three fields on this form are required. 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:
|
||||
If you 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 changed from green to red.
|
||||
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We'll customize the colors and location of the "required" bar with standard CSS.
|
||||
You can customize the colors and location of the "required" bar with standard CSS.
|
||||
|
||||
:marked
|
||||
We'll build this form in small steps:
|
||||
You'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 form’s submit button until the form is valid.
|
||||
1. Show and hide validation-error messages.
|
||||
1. Handle form submission with *ngSubmit*.
|
||||
1. Disable the form’s *Submit* button until the form is valid.
|
||||
|
||||
:marked
|
||||
## Setup
|
||||
|
@ -88,11 +87,11 @@ figure.image-display
|
|||
|
||||
## Create the Hero model class
|
||||
|
||||
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.
|
||||
As users enter form data, you'll capture their changes and update an instance of a model.
|
||||
You can't lay out the form until you know what the model looks like.
|
||||
|
||||
A model can be as simple as a "property bag" that holds facts about a thing of application importance.
|
||||
That describes well our `Hero` class with its three required fields (`id`, `name`, `power`)
|
||||
That describes well the `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:
|
||||
|
@ -100,14 +99,14 @@ figure.image-display
|
|||
+makeExample('src/app/hero.ts')
|
||||
|
||||
:marked
|
||||
It's an anemic model with few requirements and no behavior. Perfect for our demo.
|
||||
It's an anemic model with few requirements and no behavior. Perfect for the 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.
|
||||
automatically assigns the parameter’s value to that field when you create heroes.
|
||||
|
||||
The `alterEgo` is optional, so the constructor lets us omit it; note the (?) in `alterEgo?`.
|
||||
The `alterEgo` is optional, so the constructor lets you omit it; note the question mark (?) in `alterEgo?`.
|
||||
|
||||
We can create a new hero like this:
|
||||
You can create a new hero like this:
|
||||
|
||||
+makeExcerpt('src/app/hero-form.component.ts', 'SkyDog', '')
|
||||
|
||||
|
@ -117,7 +116,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.
|
||||
We begin with the class because it states, in brief, what the hero editor can do.
|
||||
Begin with the class because it states, in brief, what the hero editor can do.
|
||||
|
||||
Create the following file with the given content:
|
||||
|
||||
|
@ -125,45 +124,45 @@ figure.image-display
|
|||
|
||||
:marked
|
||||
There’s nothing special about this component, nothing form-specific,
|
||||
nothing to distinguish it from any component we've written before.
|
||||
nothing to distinguish it from any component you've written before.
|
||||
|
||||
Understanding this component requires only the Angular concepts covered in previous guides.
|
||||
Understanding this component requires only the Angular concepts covered in previous pages.
|
||||
|
||||
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.
|
||||
- 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.
|
||||
|
||||
### Why the separate template file?
|
||||
|
||||
Why don't we write the template inline in the component file as we often do elsewhere?
|
||||
Why don't you write the template inline in the component file as you often do elsewhere?
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Form templates tend to be quite large even when displaying a small number of fields
|
||||
Form templates tend to be large, even when displaying a small number of fields,
|
||||
so it's usually best to put the HTML template in a separate file.
|
||||
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`.
|
||||
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`.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Revise *app.module.ts*
|
||||
|
||||
`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`.
|
||||
`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`.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Replace the contents of the "QuickStart" version with the following:
|
||||
+makeExample('forms/ts/src/app/app.module.ts', null, 'src/app/app.module.ts')
|
||||
|
@ -173,24 +172,26 @@ figure.image-display
|
|||
:marked
|
||||
There are three changes:
|
||||
|
||||
1. We import `FormsModule` and our new `HeroFormComponent`.
|
||||
1. You import `FormsModule` and the new `HeroFormComponent`.
|
||||
|
||||
1. We add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives our application
|
||||
1. You add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives the application
|
||||
access to all of the template-driven forms features, including `ngModel`.
|
||||
|
||||
1. We add the `HeroFormComponent` to the list of `declarations` defined in the `ngModule` decorator. This makes
|
||||
1. You 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.
|
||||
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?
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Revise *app.component.ts*
|
||||
|
||||
`AppComponent` is the application's root component. It will host our new `HeroFormComponent`.
|
||||
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
|
||||
|
||||
Replace the contents of the "QuickStart" version with the following:
|
||||
|
||||
|
@ -201,40 +202,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 will display the hero form when the application component is loaded.
|
||||
We've also dropped the `name` field from the class body.
|
||||
This displays the hero form when the application component is loaded.
|
||||
You've also dropped the `name` field from the class body.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Create an initial HTML form template
|
||||
|
||||
Create the new template file with the following contents:
|
||||
Create the template file with the following contents:
|
||||
|
||||
+makeExample('src/app/hero-form.component.html', 'start')
|
||||
|
||||
:marked
|
||||
That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and
|
||||
The language is simply HTML5. You'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.
|
||||
|
||||
We've got a *Submit* button at the bottom with some classes on it for styling.
|
||||
You added a *Submit* button at the bottom with some classes on it for styling.
|
||||
|
||||
**We are not using Angular yet**. There are no bindings, no extra directives, just layout.
|
||||
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
||||
|
||||
The `container`, `form-group`, `form-control`, and `btn` classes
|
||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic.
|
||||
We're using Bootstrap to give the form a little style!
|
||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
||||
Bootstrap gives the form a little style.
|
||||
|
||||
.callout.is-important
|
||||
header Angular forms do not require a style library
|
||||
header Angular forms don't 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
|
||||
Let's add the stylesheet. Open `index.html` and add the following link to the `<head>`:
|
||||
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
|
||||
|
||||
+makeExcerpt('src/index.html', 'bootstrap')
|
||||
|
||||
|
@ -242,12 +243,12 @@ figure.image-display
|
|||
:marked
|
||||
## Add powers with _*ngFor_
|
||||
|
||||
Our hero must choose one super power from a fixed list of Agency-approved powers.
|
||||
We maintain that list internally (in `HeroFormComponent`).
|
||||
The hero must choose one superpower from a fixed list of agency-approved powers.
|
||||
You maintain that list internally (in `HeroFormComponent`).
|
||||
|
||||
We'll add a `select` to our
|
||||
You'll add a `select` to the
|
||||
form and bind the options to the `powers` list using `ngFor`,
|
||||
a technique seen previously in the [Displaying Data](./displaying-data.html) guide.
|
||||
a technique seen previously in the [Displaying Data](./displaying-data.html) page.
|
||||
|
||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||
|
||||
|
@ -256,7 +257,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;
|
||||
we display its name using the interpolation syntax.
|
||||
you display its name using the interpolation syntax.
|
||||
|
||||
.l-main-section#ngModel
|
||||
:marked
|
||||
|
@ -267,17 +268,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
|
||||
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
|
||||
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
|
||||
event binding and how to update a component property with the displayed value.
|
||||
|
||||
Now we need to display, listen, and extract at the same time.
|
||||
Now you need to display, listen, and extract at the same time.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Find the `<input>` tag for *Name* and update it like this:
|
||||
|
||||
|
@ -285,17 +286,17 @@ figure.image-display
|
|||
|
||||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
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.
|
||||
|
||||
:marked
|
||||
Focus on the binding syntax: `[(ngModel)]="..."`.
|
||||
|
||||
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
|
||||
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
|
||||
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")
|
||||
|
@ -306,29 +307,30 @@ figure.image-display
|
|||
|
||||
.l-sub-section
|
||||
:marked
|
||||
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.
|
||||
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.
|
||||
|
||||
:marked
|
||||
Notice that we also added a `name` attribute to our `<input>` tag and set it to "name"
|
||||
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
|
||||
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
|
||||
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 we assigned to the `name` attribute.
|
||||
We'll talk about `NgForm` [later in this guide](#ngForm).
|
||||
Each `FormControl` is registered under the name you assigned to the `name` attribute.
|
||||
Read more in [The NgForm directive](#ngForm), later in this page.
|
||||
|
||||
:marked
|
||||
Let's add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
||||
We'll ditch the input box binding message
|
||||
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
||||
You'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 two-way data binding works *for the entire hero model*.
|
||||
Then you can confirm that two-way data binding works *for the entire hero model*.
|
||||
|
||||
After revision, the core of our form should look like this:
|
||||
After revision, the core of the form should look like this:
|
||||
|
||||
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'ngModel-2')
|
||||
|
||||
|
@ -339,28 +341,26 @@ 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 we run the app now and changed every hero model property, the form might display like this:
|
||||
If you run the app now and change 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 our changes are reflected in the model.
|
||||
confirms that all of your 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_
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
|
||||
We can leverage those class names to change the appearance of the control.
|
||||
You 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 Control has been visited
|
||||
td The control has been visited.
|
||||
td <code>ng-touched</code>
|
||||
td <code>ng-untouched</code>
|
||||
tr
|
||||
td Control's value has changed
|
||||
td The control's value has changed.
|
||||
td <code>ng-dirty</code>
|
||||
td <code>ng-pristine</code>
|
||||
tr
|
||||
td Control's value is valid
|
||||
td The control's value is valid.
|
||||
td <code>ng-valid</code>
|
||||
td <code>ng-invalid</code>
|
||||
|
||||
:marked
|
||||
Let's temporarily add a [template reference variable](./template-syntax.html#ref-vars) named `spy`
|
||||
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 the next four steps *precisely*:
|
||||
Now run the app and look at the _Name_ input box.
|
||||
Follow these 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
|
||||
We should see the following transitions and class names:
|
||||
You 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 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.
|
||||
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.
|
||||
|
||||
**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
|
||||
|
||||
We can mark required fields and invalid data at the same time with a colored bar
|
||||
You 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
|
||||
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`:
|
||||
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`:
|
||||
|
||||
+makeExample('src/forms.css')
|
||||
|
||||
|
@ -437,64 +437,63 @@ figure.image-display
|
|||
:marked
|
||||
## Show and hide validation error messages
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Here's the way it should look when the user deletes the name:
|
||||
When the user deletes the name, the form should look like this:
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/forms/name-required-error.png" width="400px" alt="Name required")
|
||||
|
||||
:marked
|
||||
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.
|
||||
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.
|
||||
|
||||
Here's an example of adding an error message to the _name_ input box:
|
||||
Here's an example of an error message added to the _name_ input box:
|
||||
|
||||
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'name-with-error-msg')
|
||||
|
||||
:marked
|
||||
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".
|
||||
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".
|
||||
|
||||
.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.
|
||||
We set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
|
||||
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
|
||||
|
||||
:marked
|
||||
We control visibility of the name error message by binding properties of the `name`
|
||||
You 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, 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Some folks find that behavior disconcerting.
|
||||
They only want to see the message when the user makes an invalid change.
|
||||
Some developers want to the message to display only when the user makes an invalid change.
|
||||
Hiding the message while the control is "pristine" achieves that goal.
|
||||
We'll see the significance of this choice when we [add a new hero](#new-hero) to the form.
|
||||
You'll see the significance of this choice when you [add a new hero](#new-hero) to the form.
|
||||
|
||||
The hero *Alter Ego* is optional so we can leave that be.
|
||||
The hero *Alter Ego* is optional so you can leave that be.
|
||||
|
||||
Hero *Power* selection is required.
|
||||
We can add the same kind of error handling to the `<select>` if we want,
|
||||
You can add the same kind of error handling to the `<select>` if you want,
|
||||
but it's not imperative because the selection box already constrains the
|
||||
power to valid values.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
+makeExample('forms/ts/src/app/hero-form.component.html',
|
||||
'new-hero-button-no-reset',
|
||||
|
@ -508,18 +507,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; we haven't changed anything yet.
|
||||
The error messages are hidden because the form is pristine; you haven't changed anything yet.
|
||||
|
||||
Enter a name and click *New Hero* again.
|
||||
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?
|
||||
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?
|
||||
|
||||
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
|
||||
The form remembers that we entered a name before clicking *New Hero*.
|
||||
The form remembers that you entered a name before clicking *New Hero*.
|
||||
Replacing the hero object *did not restore the pristine state* of the form controls.
|
||||
|
||||
We have to clear all of the flags imperatively which we can do
|
||||
You have to clear all of the flags imperatively, which you can do
|
||||
by calling the form's `reset()` method after calling the `newHero()` method.
|
||||
|
||||
+makeExample('forms/ts/src/app/hero-form.component.html',
|
||||
|
@ -527,14 +526,14 @@ figure.image-display
|
|||
'src/app/hero-form.component.html (Reset the form)')
|
||||
|
||||
:marked
|
||||
Now clicking "New Hero" both resets the form and its control flags.
|
||||
Now clicking "New Hero" resets both 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"`).
|
||||
|
||||
|
@ -545,8 +544,8 @@ figure.image-display
|
|||
+makeExcerpt('forms/ts/src/app/hero-form.component.html (ngSubmit)')
|
||||
|
||||
:marked
|
||||
We slipped in something extra there at the end! We defined a
|
||||
template reference variable, **`#heroForm`**, and initialized it with the value "ngForm".
|
||||
You added something extra at the end. You 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.
|
||||
|
||||
|
@ -555,35 +554,35 @@ figure.image-display
|
|||
### The _NgForm_ directive
|
||||
|
||||
What `NgForm` directive?
|
||||
We didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive!
|
||||
You didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive.
|
||||
|
||||
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
|
||||
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
||||
|
||||
The `NgForm` directive supplements the `form` element with additional features.
|
||||
It holds the controls we created for the elements with an `ngModel` directive
|
||||
and `name` attribute, and monitors their properties including their validity.
|
||||
It holds the controls you 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
|
||||
We'll bind the form's overall validity via
|
||||
You'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 we run the application now, we find that the button is enabled
|
||||
— although it doesn't do anything useful yet.
|
||||
If you run the application now, you find that the button is enabled—although
|
||||
it doesn't do anything useful yet.
|
||||
|
||||
Now if we delete the Name, we violate the "required" rule, which
|
||||
Now if you delete the Name, you violate the "required" rule, which
|
||||
is duly noted in the error message.
|
||||
The Submit button is also disabled.
|
||||
The *Submit* button is also disabled.
|
||||
|
||||
Not impressed? Think about it for a moment. What would we have to do to
|
||||
Not impressed? Think about it for a moment. What would you have to do to
|
||||
wire the button's enable/disabled state to the form's validity without Angular's help?
|
||||
|
||||
For us, it was as simple as:
|
||||
For you, it was as simple as this:
|
||||
|
||||
1. Define a template reference variable on the (enhanced) form element.
|
||||
2. Refer to that variable in a button many lines away.
|
||||
|
@ -597,65 +596,63 @@ figure.image-display
|
|||
.l-sub-section
|
||||
:marked
|
||||
An unsurprising observation for a demo. To be honest,
|
||||
jazzing it up won't teach us anything new about forms.
|
||||
But this is an opportunity to exercise some of our newly won
|
||||
jazzing it up won't teach you anything new about forms.
|
||||
But this is an opportunity to exercise some of your newly won
|
||||
binding skills.
|
||||
If you aren't interested, go ahead and skip to this guide's conclusion.
|
||||
If you aren't interested, skip to this page's conclusion.
|
||||
|
||||
:marked
|
||||
Let's do something more strikingly visual.
|
||||
Let's hide the data entry area and display something else.
|
||||
For a more strikingly visual effect,
|
||||
hide the data entry area and display something else.
|
||||
|
||||
Start by wrapping the form in a `<div>` and bind
|
||||
Wrap 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 we submit the form,
|
||||
`submitted` property is false until you submit the form,
|
||||
as this fragment from the `HeroFormComponent` shows:
|
||||
|
||||
+makeExcerpt('src/app/hero-form.component.ts', 'submitted')
|
||||
|
||||
:marked
|
||||
When we click the Submit button, the `submitted` flag becomes true and the form disappears
|
||||
When you 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 we just wrote:
|
||||
Add the following HTML below the `<div>` wrapper you just wrote:
|
||||
|
||||
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'submitted')
|
||||
|
||||
:marked
|
||||
There's our hero again, displayed read-only with interpolation bindings.
|
||||
There's the 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 we click the _Edit_ button, this block disappears and the editable form reappears.
|
||||
|
||||
That's as much drama as we can muster for now.
|
||||
When you click the *Edit* button, this block disappears and the editable form reappears.
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## Conclusion
|
||||
|
||||
The Angular form discussed in this guide takes advantage of the following
|
||||
The Angular form discussed in this page 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 variable’s `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.
|
||||
|
||||
Our final project folder structure should look like this:
|
||||
The final project folder structure should look like this:
|
||||
|
||||
.filetree
|
||||
.file angular-forms
|
||||
|
|
Loading…
Reference in New Issue