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>'
|
- var _decoratorLink = '<a href="#' + _decorator + '">' + _decorator + '</a>'
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Angular has a vocabulary of its own.
|
Angular has its own vocabulary.
|
||||||
Most Angular terms are everyday English words
|
Most Angular terms are common English words
|
||||||
with a specific meaning within the Angular system.
|
with a specific meaning within the Angular system.
|
||||||
|
|
||||||
This glossary lists the most prominent terms
|
This glossary lists the most prominent terms
|
||||||
and a few less familiar ones that have unusual or
|
and a few less familiar ones that have unusual or
|
||||||
unexpected definitions.
|
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)
|
[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)
|
[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
|
## Ahead-of-time (AOT) compilation
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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
|
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.
|
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.
|
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.
|
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`.
|
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`.
|
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')
|
+ifDocsFor('ts|dart')
|
||||||
:marked
|
:marked
|
||||||
|
@ -64,7 +65,7 @@ a#attribute-directives
|
||||||
other HTML elements, attributes, properties, and components. They are usually represented
|
other HTML elements, attributes, properties, and components. They are usually represented
|
||||||
as HTML attributes, hence the name.
|
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.
|
Learn about them in the [_Attribute Directives_](!{docsLatest}/guide/attribute-directives.html) guide.
|
||||||
|
|
||||||
|
@ -75,10 +76,10 @@ a#attribute-directives
|
||||||
## Barrel
|
## Barrel
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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.
|
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.
|
code-example.
|
||||||
// heroes/hero.component.ts
|
// heroes/hero.component.ts
|
||||||
export class HeroComponent {}
|
export class HeroComponent {}
|
||||||
|
@ -89,7 +90,7 @@ a#attribute-directives
|
||||||
// heroes/hero.service.ts
|
// heroes/hero.service.ts
|
||||||
export class HeroService {}
|
export class HeroService {}
|
||||||
:marked
|
:marked
|
||||||
Without a barrel, a consumer would need three import statements:
|
Without a barrel, a consumer needs three import statements:
|
||||||
code-example.
|
code-example.
|
||||||
import { HeroComponent } from '../heroes/hero.component.ts';
|
import { HeroComponent } from '../heroes/hero.component.ts';
|
||||||
import { Hero } from '../heroes/hero.model.ts';
|
import { Hero } from '../heroes/hero.model.ts';
|
||||||
|
@ -109,18 +110,18 @@ a#attribute-directives
|
||||||
|
|
||||||
.alert.is-important
|
.alert.is-important
|
||||||
:marked
|
: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
|
:marked
|
||||||
## Binding
|
## Binding
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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.
|
binding an HTML object property to a data object property.
|
||||||
|
|
||||||
May refer to a [dependency injection](#dependency-injection) binding
|
Sometimes refers to a [dependency-injection](#dependency-injection) binding
|
||||||
between a "token", also referred to as a "key", and a dependency [provider](#provider).
|
between a "token"—also referred to as a "key"—and a dependency [provider](#provider).
|
||||||
This more rare usage should be clear in context.
|
When using this more rare usage, be clear in context.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
## Bootstrap
|
## 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.
|
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.
|
For more information, see the [Setup](!{docsLatest}/guide/setup.html) page.
|
||||||
:marked
|
: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
|
.l-main-section#C
|
||||||
:marked
|
: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
|
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_.
|
_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).
|
camelCase is also known as *lower camel case* to distinguish it from *upper camel case*, or [PascalCase](#pascalcase).
|
||||||
When you see "camelCase" in this documentation it always means *lower camel case*.
|
In Angular documentation, "camelCase" always means *lower camel case*.
|
||||||
|
|
||||||
a#component
|
a#component
|
||||||
:marked
|
:marked
|
||||||
|
@ -155,9 +156,9 @@ a#component
|
||||||
The *component* is one of the most important building blocks in the Angular system.
|
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).
|
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
|
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.
|
as a view.
|
||||||
|
|
||||||
Those familiar with "MVC" and "MVVM" patterns will recognize
|
Those familiar with "MVC" and "MVVM" patterns will recognize
|
||||||
|
@ -169,7 +170,7 @@ a#component
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The practice of writing compound words or phrases such that each word is separated by a dash or hyphen (`-`).
|
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
|
[Directive](#directive) selectors (like `my-app`) <span if-docs="ts">and
|
||||||
the root of filenames (such as `hero-list.component.ts`)</span> are often
|
the root of filenames (such as `hero-list.component.ts`)</span> are often
|
||||||
|
@ -180,17 +181,18 @@ a#component
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Applications display data values to a user and respond to user
|
Applications display data values to a user and respond to user
|
||||||
actions (clicks, touches, keystrokes).
|
actions (such as clicks, touches, and keystrokes).
|
||||||
|
|
||||||
Instead of manually pushing application data values into HTML, attaching
|
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
|
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
|
updating application data values.
|
||||||
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.
|
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).
|
* [Interpolation](!{docsLatest}/guide/template-syntax.html#interpolation).
|
||||||
* [Property binding](!{docsLatest}/guide/template-syntax.html#property-binding).
|
* [Property binding](!{docsLatest}/guide/template-syntax.html#property-binding).
|
||||||
* [Event binding](!{docsLatest}/guide/template-syntax.html#event-binding).
|
* [Event binding](!{docsLatest}/guide/template-syntax.html#event-binding).
|
||||||
|
@ -208,14 +210,14 @@ a#component
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
block decorator-defn
|
block decorator-defn
|
||||||
:marked
|
: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.
|
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
|
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.
|
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
|
The scope of a decorator is limited to the language feature
|
||||||
that it decorates. None of the decorations shown here will "leak" to other
|
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
|
.alert.is-important
|
||||||
:marked
|
:marked
|
||||||
|
@ -237,7 +239,10 @@ a#component
|
||||||
## Dependency injection
|
## Dependency injection
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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
|
for creating and delivering parts of an application to other
|
||||||
parts of an application that request them.
|
parts of an application that request them.
|
||||||
|
|
||||||
|
@ -246,23 +251,26 @@ a#component
|
||||||
|
|
||||||
These parts often rely on other parts. An Angular [component](#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 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
|
part "A" relies on another part "B," you say that "A" depends on "B" and
|
||||||
that "B" is a dependency of "A".
|
that "B" is a dependency of "A."
|
||||||
|
|
||||||
You can ask a "dependency injection system" to create "A"
|
You can ask a "dependency injection system" to create "A"
|
||||||
and it will handle all of "A"s dependencies.
|
for us and handle all the dependencies.
|
||||||
If "A" needs "B" and "B" needs "C", the system resolves that chain of 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 returns a fully prepared instance of "A."
|
||||||
|
|
||||||
|
|
||||||
Angular provides and relies upon its own sophisticated
|
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
|
to assemble and run applications by "injecting" application parts
|
||||||
into other application parts where and when needed.
|
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.
|
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
|
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
|
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.
|
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
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
An Angular class responsible for creating, reshaping, and interacting with HTML elements
|
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,
|
When Angular finds a directive in an HTML template,
|
||||||
it creates the matching directive class instance
|
it creates the matching directive class instance
|
||||||
and gives the instance control over that portion of the browser DOM.
|
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
|
as if you were writing native HTML. In this way, directives become extensions of
|
||||||
HTML itself.
|
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.
|
render application [views](#view). Components are usually represented as HTML elements.
|
||||||
They are the building blocks of an Angular application and the
|
They are the building blocks of an Angular application.
|
||||||
developer can expect to write a lot of them.
|
|
||||||
|
|
||||||
1. [Attribute directives](#attribute-directive) that can listen to and modify the behavior of
|
1. [Attribute directives](#attribute-directive) can listen to and modify the behavior of
|
||||||
HTML elements, components, and other directives. They are usually represented
|
other HTML elements, attributes, properties, and components. They are usually represented
|
||||||
as HTML attributes, hence the name.
|
as HTML attributes, hence the name.
|
||||||
|
|
||||||
1. [Structural directives](#structural-directive) that
|
1. [Structural directives](#structural-directive) are responsible for
|
||||||
shape or reshape HTML layout, typically by adding and removing elements in the DOM.
|
shaping or reshaping HTML layout, typically by adding, removing, or manipulating
|
||||||
|
elements and their children.
|
||||||
|
|
||||||
|
|
||||||
.l-main-section#E
|
.l-main-section#E
|
||||||
|
|
||||||
|
@ -328,32 +340,31 @@ a#directives
|
||||||
|
|
||||||
The latest approved version of JavaScript is
|
The latest approved version of JavaScript is
|
||||||
[ECMAScript 2016](http://www.ecma-international.org/ecma-262/7.0/)
|
[ECMAScript 2016](http://www.ecma-international.org/ecma-262/7.0/)
|
||||||
(AKA "ES2016" or "ES7") and many Angular developers write their applications
|
(also known as "ES2016" or "ES7"). Many Angular developers write their applications
|
||||||
either in this version of the language or a dialect that strives to be
|
in ES7 or a dialect that strives to be
|
||||||
compatible with it, such as [TypeScript](#typescript).
|
compatible with it, such as [TypeScript](#typescript).
|
||||||
|
|
||||||
Most modern browsers today only support the much older "ECMAScript 5" (AKA ES5) standard.
|
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)"
|
Applications written in ES2016, ES2015, or one of their dialects must be [transpiled](#transpile)
|
||||||
to ES5 JavaScript.
|
to ES5 JavaScript.
|
||||||
|
|
||||||
Angular developers may choose to write in ES5 directly.
|
Angular developers can write in ES5 directly.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
## ES2015
|
## ES2015
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Short hand for [ECMAScript](#ecmascript) 2015.
|
Short hand for [ECMAScript](#ecmascript) 2015.
|
||||||
:marked
|
|
||||||
## ES6
|
|
||||||
.l-sub-section
|
|
||||||
:marked
|
|
||||||
Short hand for [ECMAScript](#ecmascript) 2015.
|
|
||||||
:marked
|
:marked
|
||||||
## ES5
|
## ES5
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Short hand for [ECMAScript](#ecmascript) 5, the version of JavaScript run by most modern browsers.
|
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#F
|
||||||
a#G
|
a#G
|
||||||
|
@ -363,15 +374,15 @@ a#H
|
||||||
## Injector
|
## Injector
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
An object in the Angular [dependency injection system](#dependency-injection)
|
An object in the Angular [dependency-injection system](#dependency-injection)
|
||||||
that can find a named "dependency" in its cache or create such a thing
|
that can find a named dependency in its cache or create a dependency
|
||||||
with a registered [provider](#provider).
|
with a registered [provider](#provider).
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
## Input
|
## Input
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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).
|
[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
|
Data values flow *into* this property from the data source identified
|
||||||
in the template expression to the right of the equal sign.
|
in the template expression to the right of the equal sign.
|
||||||
|
@ -402,8 +413,8 @@ a#jit
|
||||||
## Just-in-time (JIT) compilation
|
## Just-in-time (JIT) compilation
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
With Angular _just-in-time_ bootstrapping you compile your components<span if-docs="ts"> and modules</span> in the browser
|
A bootstrapping method of compiling components<span if-docs="ts"> and modules</span> in the browser
|
||||||
and launch the application dynamically. This is a good choice during development.
|
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.
|
Consider using the [ahead-of-time](#aot) mode for production apps.
|
||||||
|
|
||||||
.l-main-section#K
|
.l-main-section#K
|
||||||
|
@ -428,14 +439,14 @@ a#jit
|
||||||
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
||||||
|
|
||||||
Angular calls these hook methods in the following order:
|
Angular calls these hook methods in the following order:
|
||||||
* `ngOnChanges` - when an [input](#input)/[output](#output) binding value changes.
|
* `ngOnChanges`: when an [input](#input)/[output](#output) binding value changes.
|
||||||
* `ngOnInit` - after the first `ngOnChanges`.
|
* `ngOnInit`: after the first `ngOnChanges`.
|
||||||
* `ngDoCheck` - developer's custom change detection.
|
* `ngDoCheck`: developer's custom change detection.
|
||||||
* `ngAfterContentInit` - after component content initialized.
|
* `ngAfterContentInit`: after component content initialized.
|
||||||
* `ngAfterContentChecked` - after every check of component content.
|
* `ngAfterContentChecked`: after every check of component content.
|
||||||
* `ngAfterViewInit` - after component's view(s) are initialized.
|
* `ngAfterViewInit`: after a component's views are initialized.
|
||||||
* `ngAfterViewChecked` - after every check of a component's view(s).
|
* `ngAfterViewChecked`: after every check of a component's views.
|
||||||
* `ngOnDestroy` - just before the directive is destroyed.
|
* `ngOnDestroy`: just before the directive is destroyed.
|
||||||
|
|
||||||
Read more in the [Lifecycle Hooks](!{docsLatest}/guide/lifecycle-hooks.html) page.
|
Read more in the [Lifecycle Hooks](!{docsLatest}/guide/lifecycle-hooks.html) page.
|
||||||
|
|
||||||
|
@ -447,35 +458,36 @@ a#jit
|
||||||
block module-defn
|
block module-defn
|
||||||
.alert.is-important
|
.alert.is-important
|
||||||
:marked
|
:marked
|
||||||
In Angular, there are two types of modules:
|
Angular has the following types of modules:
|
||||||
- [Angular modules](#angular-module).
|
- [Angular modules](#angular-module).
|
||||||
For details and examples, see the [Angular Modules](!{docsLatest}/guide/ngmodule.html) page.
|
For details and examples, see the [Angular Modules](!{docsLatest}/guide/ngmodule.html) page.
|
||||||
- ES2015 modules, as described in this section.
|
- ES2015 modules, as described in this section.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
A cohesive block of code dedicated to a single purpose.
|
||||||
|
|
||||||
Angular apps are modular.
|
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.
|
In general, you assemble an 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.
|
||||||
A module **exports** something of value in that code, typically one thing such as a class.
|
<!-- CF: Is there a more descriptive word than "thing" that can be used here? Perhaps "element"? -->
|
||||||
A module that needs that thing, **imports** it.
|
|
||||||
|
|
||||||
The structure of Angular modules and the import/export syntax
|
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).
|
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
|
An application that adheres to this standard requires a module loader to
|
||||||
load modules on request, and resolve inter-module dependencies.
|
load modules on request and resolve inter-module dependencies.
|
||||||
Angular does not ship with a module loader and does not have a preference
|
Angular doesn't include a module loader and doesn't have a preference
|
||||||
for any particular 3rd party library (although most examples use SystemJS).
|
for any particular third-party library (although most examples use SystemJS).
|
||||||
You may pick any module library that conforms to the standard.
|
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.
|
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)
|
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`.
|
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
|
a#N
|
||||||
.l-main-section#O
|
.l-main-section#O
|
||||||
|
@ -485,7 +497,7 @@ a#N
|
||||||
## Observable
|
## Observable
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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 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.
|
Observables are used within Angular itself, including Angular's event system and its http client service.
|
||||||
|
|
||||||
|
@ -496,8 +508,9 @@ a#N
|
||||||
## Output
|
## Output
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
A directive property that can be the ***target*** of
|
A directive property that can be the *target* of event binding
|
||||||
[event binding](!{docsLatest}/guide/template-syntax.html#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
|
Events stream *out* of this property to the receiver identified
|
||||||
in the template expression to the right of the equal sign.
|
in the template expression to the right of the equal sign.
|
||||||
|
|
||||||
|
@ -509,14 +522,17 @@ a#N
|
||||||
## PascalCase
|
## PascalCase
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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
|
:marked
|
||||||
## Pipe
|
## Pipe
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
|
|
||||||
An Angular pipe is a function that transforms input values to output values for
|
An Angular pipe is a function that transforms input values to output values for
|
||||||
display in a [view](#view).
|
display in a [view](#view).
|
||||||
Here's an example that uses the built-in `currency` pipe to display
|
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}}
|
<label>Price: </label>{{product.price | currency}}
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
|
|
||||||
You can also write your own custom pipes.
|
You can also write your own custom pipes.
|
||||||
Read more in the page on [pipes](!{docsLatest}/guide/pipes.html).
|
Read more in the page on [pipes](!{docsLatest}/guide/pipes.html).
|
||||||
|
|
||||||
|
@ -546,15 +563,15 @@ a#Q
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
A technique for building Angular forms through code in a component.
|
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:
|
When building reactive forms:
|
||||||
- The "source of truth" is the component. The validation is defined using code in the component.
|
- 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`.
|
- Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
|
||||||
- The template input elements do *not* use `ngModel`.
|
- 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
|
:marked
|
||||||
## Router
|
## Router
|
||||||
|
@ -565,11 +582,11 @@ a#Q
|
||||||
and performing other similar actions that cause the application to
|
and performing other similar actions that cause the application to
|
||||||
replace one view with another.
|
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.
|
of views.
|
||||||
+ifDocsFor('ts|js')
|
+ifDocsFor('ts|js')
|
||||||
:marked
|
: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.
|
of a `RouterConfig` that defines routes to views.
|
||||||
|
|
||||||
A [routing component's](#routing-component) template has a `RouterOutlet` element
|
A [routing component's](#routing-component) template has a `RouterOutlet` element
|
||||||
|
@ -604,31 +621,23 @@ a#Q
|
||||||
## Scoped package
|
## Scoped package
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Angular modules are delivered within *scoped packages* such as `@angular/core`, `@angular/common`, `@angular/platform-browser-dynamic`,
|
A way to group related *npm* packages.
|
||||||
`@angular/http`, and `@angular/router`.
|
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,
|
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', '')
|
+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
|
:marked
|
||||||
## Service
|
## Service
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
|
|
||||||
For data or logic that is not associated
|
For data or logic that is not associated
|
||||||
with a specific view or that you want to share across components, build services.
|
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,
|
independent from any specific view,
|
||||||
provide shared data or logic across components, or encapsulate external interactions.
|
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.
|
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-directive
|
||||||
a#structural-directives
|
a#structural-directives
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
## Structural directives
|
## Structural directives
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
|
@ -658,8 +680,8 @@ a#structural-directives
|
||||||
## Template
|
## Template
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
A template is a chunk of HTML that Angular uses to render a [view](#view) with
|
A chunk of HTML that Angular uses to render a [view](#view) with
|
||||||
the support and continuing guidance of an Angular [directive](#directive),
|
the support and guidance of an Angular [directive](#directive),
|
||||||
most notably a [component](#component).
|
most notably a [component](#component).
|
||||||
|
|
||||||
|
|
||||||
|
@ -673,11 +695,11 @@ a#structural-directives
|
||||||
|
|
||||||
When building template-driven forms:
|
When building template-driven forms:
|
||||||
- The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
|
- 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.
|
- 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`.
|
- 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
|
Read about how to build template-driven forms
|
||||||
in the [Forms](!{docsLatest}/guide/forms.html) page.
|
in the [Forms](!{docsLatest}/guide/forms.html) page.
|
||||||
|
@ -686,18 +708,19 @@ a#structural-directives
|
||||||
## Template expression
|
## Template expression
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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).
|
a [data binding](#data-binding).
|
||||||
|
|
||||||
Read about how to write template expressions
|
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
|
:marked
|
||||||
## Transpile
|
## Transpile
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
The process of transforming code written in one form of JavaScript
|
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
|
:marked
|
||||||
## TypeScript
|
## TypeScript
|
||||||
|
@ -706,15 +729,15 @@ a#structural-directives
|
||||||
A version of JavaScript that supports most [ECMAScript 2015](#es2015)
|
A version of JavaScript that supports most [ECMAScript 2015](#es2015)
|
||||||
language features such as [decorators](#decorator).
|
language features such as [decorators](#decorator).
|
||||||
|
|
||||||
TypeScript is also noteable for its optional typing system, which enables
|
TypeScript is also notable for its optional typing system, which provides
|
||||||
compile-time type checking and strong tooling support (for example, "intellisense",
|
compile-time type checking and strong tooling support (such as "intellisense,"
|
||||||
code completion, refactoring, and intelligent search). Many code editors
|
code completion, refactoring, and intelligent search). Many code editors
|
||||||
and IDEs support TypeScript either natively or with plugins.
|
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).
|
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
|
a#U
|
||||||
.l-main-section#V
|
.l-main-section#V
|
||||||
|
@ -723,7 +746,7 @@ a#U
|
||||||
## View
|
## View
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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.
|
to user actions such as clicks, mouse moves, and keystrokes.
|
||||||
|
|
||||||
Angular renders a view under the control of one or more [directives](#directive),
|
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
|
The component plays such a prominent role that it's often
|
||||||
convenient to refer to a component as a view.
|
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
|
dynamically as the user navigates through the application, typically
|
||||||
under the control of a [router](#router).
|
under the control of a [router](#router).
|
||||||
|
|
||||||
|
@ -745,11 +768,11 @@ a#Y
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
block zone-defn
|
block zone-defn
|
||||||
:marked
|
:marked
|
||||||
Zones are a mechanism for encapsulating and intercepting
|
A mechanism for encapsulating and intercepting
|
||||||
a JavaScript application's asynchronous activity.
|
a JavaScript application's asynchronous activity.
|
||||||
|
|
||||||
The browser DOM and JavaScript have a limited number
|
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
|
[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)
|
[XHR](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)
|
||||||
calls to remote servers.
|
calls to remote servers.
|
||||||
|
@ -758,7 +781,7 @@ a#Y
|
||||||
to take action before and after the async activity finishes.
|
to take action before and after the async activity finishes.
|
||||||
|
|
||||||
Angular runs your application in a zone where it can respond to
|
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).
|
the information it displays via [data bindings](#data-binding).
|
||||||
|
|
||||||
Learn more about zones in this
|
Learn more about zones in this
|
||||||
|
|
|
@ -1,84 +1,83 @@
|
||||||
include ../_util-fns
|
include ../_util-fns
|
||||||
|
|
||||||
:marked
|
: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.
|
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.
|
In developing a form, it's important to create a data-entry experience that guides the
|
||||||
It's more challenging to create a cohesive data entry experience that guides the
|
user efficiently and effectively through the workflow.
|
||||||
user efficiently and effectively through the workflow behind the form.
|
|
||||||
|
|
||||||
*That* takes design skills that are, to be frank, well out of scope for this 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
|
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
|
||||||
**two-way data binding, change tracking, validation, and error handling**
|
|
||||||
... which we shall cover in this guide on Angular forms.
|
|
||||||
|
|
||||||
We will build a simple form from scratch, one step at a time. Along the way we'll learn how to:
|
- Build an Angular form with a component and template.
|
||||||
|
- 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
|
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||||
- 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
|
.l-main-section#template-driven
|
||||||
:marked
|
:marked
|
||||||
## Template-driven forms
|
## Template-driven forms
|
||||||
|
|
||||||
Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
|
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 guide.
|
the form-specific directives and techniques described in this page.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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
|
:marked
|
||||||
We can build almost any form we need with an Angular template — login forms, contact forms, pretty much any business form.
|
You can build almost any form with an Angular template—login forms, contact forms, and 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 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.
|
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
|
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd
|
||||||
otherwise wrestle with ourselves.
|
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
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form")
|
img(src="/resources/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Here at the *Hero Employment Agency* we use this form to maintain personal information about heroes.
|
The *Hero Employment Agency* uses 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!
|
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.
|
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
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required")
|
img(src="/resources/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required")
|
||||||
|
|
||||||
:marked
|
: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
|
.l-sub-section
|
||||||
:marked
|
: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
|
: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 `Hero` model class.
|
||||||
1. Create the component that controls the form.
|
1. Create the component that controls the form.
|
||||||
1. Create a template with the initial form layout.
|
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. 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 a `name` attribute to each form-input control.
|
||||||
1. Add custom CSS to provide visual feedback.
|
1. Add custom CSS to provide visual feedback.
|
||||||
1. Show and hide validation error messages.
|
1. Show and hide validation-error messages.
|
||||||
1. Handle form submission with **ngSubmit**.
|
1. Handle form submission with *ngSubmit*.
|
||||||
1. Disable the form’s submit button until the form is valid.
|
1. Disable the form’s *Submit* button until the form is valid.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
## Setup
|
## Setup
|
||||||
|
@ -88,11 +87,11 @@ figure.image-display
|
||||||
|
|
||||||
## Create the Hero model class
|
## Create the Hero model class
|
||||||
|
|
||||||
As users enter form data, we'll capture their changes and update an instance of a model.
|
As users enter form data, you'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.
|
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.
|
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`).
|
and one optional field (`alterEgo`).
|
||||||
|
|
||||||
In the `!{_appDir}` directory, create the following file with the given content:
|
In the `!{_appDir}` directory, create the following file with the given content:
|
||||||
|
@ -100,14 +99,14 @@ figure.image-display
|
||||||
+makeExample('src/app/hero.ts')
|
+makeExample('src/app/hero.ts')
|
||||||
|
|
||||||
:marked
|
: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
|
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', '')
|
+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_
|
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
||||||
to handle data and user interactions programmatically.
|
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:
|
Create the following file with the given content:
|
||||||
|
|
||||||
|
@ -125,45 +124,45 @@ figure.image-display
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
There’s nothing special about this component, nothing form-specific,
|
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.
|
- The code imports the Angular core library and the `Hero` model you 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.
|
- The `@Component` selector value of "hero-form" means you 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`.
|
- 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.
|
- 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.
|
- You 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
|
Down the road, you can inject a data service to get and save real data
|
||||||
or perhaps expose these properties as
|
or perhaps expose these properties as inputs and outputs
|
||||||
[inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a
|
(see [Input and output properties](./template-syntax.html#inputs-outputs) on the
|
||||||
parent component. None of this concerns us now and these future changes won't affect our form.
|
[Template Syntax](./template-syntax.html) page) for binding to a
|
||||||
1. We threw in a `diagnostic` property to return a JSON representation of our model.
|
parent component. This is not a concern now and these future changes won't affect the form.
|
||||||
It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later.
|
- 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 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.
|
There is no "right" answer for all occasions. Inline templates are useful when they are short.
|
||||||
Most form templates won't be short. TypeScript and JavaScript files generally aren't the best place to
|
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 are much help with files that have a mix of HTML and code.
|
write (or read) large stretches of HTML, and few editors 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 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.
|
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
|
You'll write that template file in a moment. First,
|
||||||
and revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
|
revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Revise *app.module.ts*
|
## 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
|
`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 our `HeroFormComponent`.
|
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
|
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
|
||||||
`imports` for our application module before we can use forms.
|
`imports` for the application module before you can use forms.
|
||||||
|
|
||||||
Replace the contents of the "QuickStart" version with the following:
|
Replace the contents of the "QuickStart" version with the following:
|
||||||
+makeExample('forms/ts/src/app/app.module.ts', null, 'src/app/app.module.ts')
|
+makeExample('forms/ts/src/app/app.module.ts', null, 'src/app/app.module.ts')
|
||||||
|
@ -173,24 +172,26 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
There are three changes:
|
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`.
|
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.
|
the `HeroFormComponent` component visible throughout this module.
|
||||||
|
|
||||||
.alert.is-important
|
.alert.is-important
|
||||||
:marked
|
: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 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 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
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Revise *app.component.ts*
|
## 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:
|
Replace the contents of the "QuickStart" version with the following:
|
||||||
|
|
||||||
|
@ -201,40 +202,40 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
There are only two changes.
|
There are only two changes.
|
||||||
The `template` is simply the new element tag identified by the component's `selector` property.
|
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.
|
This displays the hero form when the application component is loaded.
|
||||||
We've also dropped the `name` field from the class body.
|
You've also dropped the `name` field from the class body.
|
||||||
|
|
||||||
.l-main-section
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Create an initial HTML form template
|
## 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')
|
+makeExample('src/app/hero-form.component.html', 'start')
|
||||||
|
|
||||||
:marked
|
: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.
|
opening them up for user input in input boxes.
|
||||||
|
|
||||||
The *Name* `<input>` control has the HTML5 `required` attribute;
|
The *Name* `<input>` control has the HTML5 `required` attribute;
|
||||||
the *Alter Ego* `<input>` control does not because `alterEgo` is optional.
|
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
|
The `container`, `form-group`, `form-control`, and `btn` classes
|
||||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic.
|
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
||||||
We're using Bootstrap to give the form a little style!
|
Bootstrap gives the form a little style.
|
||||||
|
|
||||||
.callout.is-important
|
.callout.is-important
|
||||||
header Angular forms do not require a style library
|
header Angular forms don't require a style library
|
||||||
:marked
|
:marked
|
||||||
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
|
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
|
: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')
|
+makeExcerpt('src/index.html', 'bootstrap')
|
||||||
|
|
||||||
|
@ -242,12 +243,12 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
## Add powers with _*ngFor_
|
## Add powers with _*ngFor_
|
||||||
|
|
||||||
Our hero must choose one super power from a fixed list of Agency-approved powers.
|
The hero must choose one superpower from a fixed list of agency-approved powers.
|
||||||
We maintain that list internally (in `HeroFormComponent`).
|
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`,
|
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:
|
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||||
|
|
||||||
|
@ -256,7 +257,7 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
This code repeats the `<option>` tag for each power in the list of powers.
|
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;
|
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
|
.l-main-section#ngModel
|
||||||
:marked
|
:marked
|
||||||
|
@ -267,17 +268,17 @@ figure.image-display
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding")
|
img(src="/resources/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding")
|
||||||
:marked
|
:marked
|
||||||
We don't see hero data because we are not binding to the `Hero` yet.
|
You don't see hero data because you're not binding to the `Hero` yet.
|
||||||
We know how to do that from earlier guides.
|
You know how to do that from earlier pages.
|
||||||
[Displaying Data](./displaying-data.html) taught us property binding.
|
[Displaying Data](./displaying-data.html) teaches property binding.
|
||||||
[User Input](./user-input.html) showed us how to listen for DOM events with an
|
[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.
|
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
|
You could use the techniques you already know, but
|
||||||
instead we'll introduce something new: the `[(ngModel)]` syntax, which
|
instead you'll use the new `[(ngModel)]` syntax, which
|
||||||
makes binding the form to the model super easy.
|
makes binding the form to the model easy.
|
||||||
|
|
||||||
Find the `<input>` tag for *Name* and update it like this:
|
Find the `<input>` tag for *Name* and update it like this:
|
||||||
|
|
||||||
|
@ -285,17 +286,17 @@ figure.image-display
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
We added a diagnostic interpolation after the input tag
|
You added a diagnostic interpolation after the input tag
|
||||||
so we can see what we're doing.
|
so you can see what you're doing.
|
||||||
We left ourselves a note to throw it away when we're done.
|
You left yourself a note to throw it away when you're done.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Focus on the binding syntax: `[(ngModel)]="..."`.
|
Focus on the binding syntax: `[(ngModel)]="..."`.
|
||||||
|
|
||||||
If we run the app right now and started typing in the *Name* input box,
|
If you ran the app now and started typing in the *Name* input box,
|
||||||
adding and deleting characters, we'd see them appearing and disappearing
|
adding and deleting characters, you'd see them appear and disappear
|
||||||
from the interpolated text.
|
from the interpolated text.
|
||||||
At some point it might look like this.
|
At some point it might look like this:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action")
|
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
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
That's **two-way data binding**!
|
That's *two-way data binding*.
|
||||||
For more information about `[(ngModel)]` and two-way data bindings, see
|
For more information, see
|
||||||
the [Template Syntax](template-syntax.html#ngModel) page.
|
[Two-way binding with NgModel](template-syntax.html#ngModel) on the
|
||||||
|
the [Template Syntax](template-syntax.html) page.
|
||||||
|
|
||||||
:marked
|
: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.
|
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.
|
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
: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.
|
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.
|
Each `FormControl` is registered under the name you assigned to the `name` attribute.
|
||||||
We'll talk about `NgForm` [later in this guide](#ngForm).
|
Read more in [The NgForm directive](#ngForm), later in this page.
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Let's add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
||||||
We'll ditch the input box binding message
|
You'll ditch the input box binding message
|
||||||
and add a new binding (at the top) to the component's `diagnostic` property.
|
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')
|
+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.
|
- Each input element has a `name` property that is required by Angular forms to register the control with the form.
|
||||||
|
|
||||||
:marked
|
: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
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action")
|
img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The diagnostic near the top of the form
|
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
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Track control state and validity with _ngModel_
|
## 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 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.
|
||||||
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.
|
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
|
table
|
||||||
tr
|
tr
|
||||||
|
@ -368,27 +368,27 @@ table
|
||||||
th Class if true
|
th Class if true
|
||||||
th Class if false
|
th Class if false
|
||||||
tr
|
tr
|
||||||
td Control has been visited
|
td The control has been visited.
|
||||||
td <code>ng-touched</code>
|
td <code>ng-touched</code>
|
||||||
td <code>ng-untouched</code>
|
td <code>ng-untouched</code>
|
||||||
tr
|
tr
|
||||||
td Control's value has changed
|
td The control's value has changed.
|
||||||
td <code>ng-dirty</code>
|
td <code>ng-dirty</code>
|
||||||
td <code>ng-pristine</code>
|
td <code>ng-pristine</code>
|
||||||
tr
|
tr
|
||||||
td Control's value is valid
|
td The control's value is valid.
|
||||||
td <code>ng-valid</code>
|
td <code>ng-valid</code>
|
||||||
td <code>ng-invalid</code>
|
td <code>ng-invalid</code>
|
||||||
|
|
||||||
:marked
|
: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.
|
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')
|
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'ngModelName-2')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
Now run the app, and look at the _Name_ input box.
|
Now run the app and look at the _Name_ input box.
|
||||||
Follow the next four steps *precisely*:
|
Follow these steps *precisely*:
|
||||||
|
|
||||||
1. Look but don't touch.
|
1. Look but don't touch.
|
||||||
1. Click inside the name box, then click outside it.
|
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")
|
img(src="/resources/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We should see the following transitions and class names:
|
You should see the following transitions and class names:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions")
|
img(src="/resources/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The `ng-valid`/`ng-invalid` pair is the most interesting to us, because we want to send a
|
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
|
||||||
strong visual signal when the values are invalid. We also want to mark required fields.
|
strong visual signal when the values are invalid. You also want to mark required fields.
|
||||||
To create such visual feedback, let's add definitions for the `ng-*` CSS classes.
|
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
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Add custom CSS for visual feedback
|
## 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:
|
on the left of the input box:
|
||||||
|
|
||||||
figure.image-display
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form")
|
img(src="/resources/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We achieve this effect by adding these class definitions to a new `forms.css` file
|
You 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`:
|
that you add to the project as a sibling to `index.html`:
|
||||||
|
|
||||||
+makeExample('src/forms.css')
|
+makeExample('src/forms.css')
|
||||||
|
|
||||||
|
@ -437,64 +437,63 @@ figure.image-display
|
||||||
:marked
|
:marked
|
||||||
## Show and hide validation error messages
|
## Show and hide validation error messages
|
||||||
|
|
||||||
We can do better. The _Name_ input box is required and clearing it turns the bar red.
|
You can improve the form. 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.
|
That says something is wrong but the user doesn't know *what* is wrong or what to do about it.
|
||||||
We can leverage the control's state to reveal a helpful message.
|
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
|
figure.image-display
|
||||||
img(src="/resources/images/devguide/forms/name-required-error.png" width="400px" alt="Name required")
|
img(src="/resources/images/devguide/forms/name-required-error.png" width="400px" alt="Name required")
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
To achieve this effect we extend the `<input>` tag with
|
To achieve this effect, extend the `<input>` tag with the following:
|
||||||
1. a [template reference variable](./template-syntax.html#ref-vars)
|
- 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.
|
- 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')
|
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'name-with-error-msg')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We need a template reference variable to access the input box's Angular control from within the template.
|
You 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".
|
Here you created a variable called `name` and gave it the value "ngModel".
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Why "ngModel"?
|
Why "ngModel"?
|
||||||
A directive's [exportAs](../api/core/index/Directive-decorator.html) property
|
A directive's [exportAs](../api/core/index/Directive-decorator.html) property
|
||||||
tells Angular how to link the reference variable to the directive.
|
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
|
: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.
|
control to the message `<div>` element's `hidden` property.
|
||||||
|
|
||||||
+makeExcerpt('src/app/hero-form.component.html', 'hidden-error-msg', '')
|
+makeExcerpt('src/app/hero-form.component.html', 'hidden-error-msg', '')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
In this example, we hide the message when the control is valid or pristine;
|
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.
|
"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.
|
This user experience is the developer's choice. Some developers want the message to display at all times.
|
||||||
If we ignore the `pristine` state, we would hide the message only when the value is valid.
|
If you ignore the `pristine` state, you 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,
|
If you 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.
|
you'll see the error message immediately, before you've done anything.
|
||||||
|
|
||||||
Some folks find that behavior disconcerting.
|
Some developers want to the message to display only when the user makes an invalid change.
|
||||||
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.
|
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.
|
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
|
but it's not imperative because the selection box already constrains the
|
||||||
power to valid values.
|
power to valid values.
|
||||||
|
|
||||||
We'd like to add a new hero in this form.
|
Now you'll 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.
|
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',
|
+makeExample('forms/ts/src/app/hero-form.component.html',
|
||||||
'new-hero-button-no-reset',
|
'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.
|
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.
|
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.
|
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.
|
Enter a name and click *New Hero* again.
|
||||||
The app displays a **_Name is required_** error message!
|
The app displays a _Name is required_ error message.
|
||||||
We don't want error messages when we create a new (empty) hero.
|
You don't want error messages when you create a new (empty) hero.
|
||||||
Why are we getting one now?
|
Why are you getting one now?
|
||||||
|
|
||||||
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
|
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.
|
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.
|
by calling the form's `reset()` method after calling the `newHero()` method.
|
||||||
|
|
||||||
+makeExample('forms/ts/src/app/hero-form.component.html',
|
+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)')
|
'src/app/hero-form.component.html (Reset the form)')
|
||||||
|
|
||||||
:marked
|
: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
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Submit the form with _ngSubmit_
|
## Submit the form with _ngSubmit_
|
||||||
|
|
||||||
The user should be able to submit this form after filling it in.
|
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
|
does nothing on its own, but it will
|
||||||
trigger a form submit because of its type (`type="submit"`).
|
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)')
|
+makeExcerpt('forms/ts/src/app/hero-form.component.html (ngSubmit)')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
We slipped in something extra there at the end! We defined a
|
You added something extra at the end. You defined a
|
||||||
template reference variable, **`#heroForm`**, and initialized it with the value "ngForm".
|
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.
|
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
|
### The _NgForm_ directive
|
||||||
|
|
||||||
What `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.
|
The `NgForm` directive supplements the `form` element with additional features.
|
||||||
It holds the controls we created for the elements with an `ngModel` directive
|
It holds the controls you created for the elements with an `ngModel` directive
|
||||||
and `name` attribute, and monitors their properties including their validity.
|
and `name` attribute, and monitors their properties, including their validity.
|
||||||
It also has its own `valid` property which is true only *if every contained
|
It also has its own `valid` property which is true only *if every contained
|
||||||
control* is valid.
|
control* is valid.
|
||||||
|
|
||||||
:marked
|
: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
|
the `heroForm` variable to the button's `disabled` property
|
||||||
using an event binding. Here's the code:
|
using an event binding. Here's the code:
|
||||||
|
|
||||||
+makeExcerpt('src/app/hero-form.component.html', 'submit-button', '')
|
+makeExcerpt('src/app/hero-form.component.html', 'submit-button', '')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
If we run the application now, we find that the button is enabled
|
If you run the application now, you find that the button is enabled—although
|
||||||
— although it doesn't do anything useful yet.
|
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.
|
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?
|
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.
|
1. Define a template reference variable on the (enhanced) form element.
|
||||||
2. Refer to that variable in a button many lines away.
|
2. Refer to that variable in a button many lines away.
|
||||||
|
@ -597,65 +596,63 @@ figure.image-display
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
An unsurprising observation for a demo. To be honest,
|
An unsurprising observation for a demo. To be honest,
|
||||||
jazzing it up won't teach us anything new about forms.
|
jazzing it up won't teach you anything new about forms.
|
||||||
But this is an opportunity to exercise some of our newly won
|
But this is an opportunity to exercise some of your newly won
|
||||||
binding skills.
|
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
|
:marked
|
||||||
Let's do something more strikingly visual.
|
For a more strikingly visual effect,
|
||||||
Let's hide the data entry area and display something else.
|
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.
|
its `hidden` property to the `HeroFormComponent.submitted` property.
|
||||||
|
|
||||||
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'edit-div')
|
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'edit-div')
|
||||||
|
|
||||||
:marked
|
:marked
|
||||||
The main form is visible from the start because the
|
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:
|
as this fragment from the `HeroFormComponent` shows:
|
||||||
|
|
||||||
+makeExcerpt('src/app/hero-form.component.ts', 'submitted')
|
+makeExcerpt('src/app/hero-form.component.ts', 'submitted')
|
||||||
|
|
||||||
:marked
|
: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.
|
as planned.
|
||||||
|
|
||||||
Now the app needs to show something else while the form is in the submitted state.
|
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')
|
+makeExcerpt('src/app/hero-form.component.html (excerpt)', 'submitted')
|
||||||
|
|
||||||
:marked
|
: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.
|
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.
|
that clears the `submitted` flag.
|
||||||
|
|
||||||
When we click the _Edit_ button, this block disappears and the editable form reappears.
|
When you 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
|
.l-main-section
|
||||||
:marked
|
:marked
|
||||||
## Conclusion
|
## 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:
|
framework features to provide support for data modification, validation, and more:
|
||||||
|
|
||||||
- An Angular HTML form template.
|
- An Angular HTML form template.
|
||||||
- A form component class with a `@Component` decorator.
|
- A form component class with a `@Component` decorator.
|
||||||
- Handling form submission by binding to the `NgForm.ngSubmit` event property.
|
- 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.
|
- `[(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.
|
- 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.
|
- 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
|
.filetree
|
||||||
.file angular-forms
|
.file angular-forms
|
||||||
|
|
Loading…
Reference in New Issue