+
+ Name via form.controls = {{showFormControls(hf)}}
+
+
+
\ No newline at end of file
diff --git a/public/docs/_examples/forms/ts/src/app/hero-form.component.ts b/public/docs/_examples/forms/ts/src/app/hero-form.component.ts
new file mode 100644
index 0000000000..672c9fc0f2
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/hero-form.component.ts
@@ -0,0 +1,48 @@
+// #docplaster
+// #docregion
+// #docregion first, final
+import {Component, CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/angular2';
+
+import { Hero } from './hero';
+
+@Component({
+ selector: 'hero-form',
+ templateUrl: 'app/hero-form.component.html',
+ // #docregion directives
+ directives: [CORE_DIRECTIVES, FORM_DIRECTIVES]
+ // #enddocregion
+})
+export class HeroFormComponent {
+
+ powers = ['Really Smart', 'Super Flexible',
+ 'Super Hot', 'Weather Changer'];
+
+ model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');
+
+ // #docregion submitted
+ submitted = false;
+
+ onSubmit() { this.submitted = true; }
+ // #enddocregion submitted
+
+ // #enddocregion final
+ // TODO: Remove this when we're done
+ get diagnostic() { return JSON.stringify(this.model); }
+ // #enddocregion first
+
+
+ //////// DO NOT SHOW IN DOCS ////////
+
+ // Reveal in html:
+ // AlterEgo via form.controls = {{showFormControls(hf)}}
+ showFormControls(form){
+ return form.controls.alterEgo &&
+ // #docregion form-controls
+ form.controls.name.value; // Dr. IQ
+ // #enddocregion form-controls
+ }
+ /////////////////////////////
+
+ // #docregion first, final
+}
+// #enddocregion first, final
\ No newline at end of file
diff --git a/public/docs/_examples/forms/ts/src/app/hero.ts b/public/docs/_examples/forms/ts/src/app/hero.ts
new file mode 100644
index 0000000000..6ce9e8af1a
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/hero.ts
@@ -0,0 +1,11 @@
+// #docregion
+export class Hero {
+
+ constructor(
+ public id: number,
+ public name: string,
+ public power: string,
+ public alterEgo?: string
+ ) { }
+
+}
\ No newline at end of file
diff --git a/public/docs/_examples/forms/ts/src/index.html b/public/docs/_examples/forms/ts/src/index.html
new file mode 100644
index 0000000000..2aa0229ab4
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+ Hero Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
\ No newline at end of file
diff --git a/public/docs/_examples/forms/ts/src/styles.css b/public/docs/_examples/forms/ts/src/styles.css
new file mode 100644
index 0000000000..d7e11405b1
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/styles.css
@@ -0,0 +1,9 @@
+/* #docregion */
+.ng-valid[required] {
+ border-left: 5px solid #42A948; /* green */
+}
+
+.ng-invalid {
+ border-left: 5px solid #a94442; /* red */
+}
+/* #enddocregion */
\ No newline at end of file
diff --git a/public/docs/_examples/forms/ts/src/tsconfig.json b/public/docs/_examples/forms/ts/src/tsconfig.json
new file mode 100644
index 0000000000..6a58b35a58
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "module": "commonjs",
+ "sourceMap": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "removeComments": false,
+ "noImplicitAny": false
+ }
+}
\ No newline at end of file
diff --git a/public/docs/ts/latest/guide/_data.json b/public/docs/ts/latest/guide/_data.json
index cb2d533147..09f639bbef 100644
--- a/public/docs/ts/latest/guide/_data.json
+++ b/public/docs/ts/latest/guide/_data.json
@@ -18,7 +18,12 @@
"title": "User Input",
"intro": "User input triggers DOM events. We listen to those events with EventBindings that funnel updated values back into our components and models."
},
-
+
+ "forms": {
+ "title": "Forms",
+ "intro": "A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors."
+ },
+
"pipes": {
"title": "Pipes",
"intro": "Pipes transform displayed values within a template"
@@ -28,11 +33,6 @@
"title": "Template Syntax",
"intro": "How to write templates that display data and consume user events with the help of data binding."
},
-
- "forms": {
- "title": "Angular 2 Forms",
- "intro": "Learn about the different approaches we can take when building forms and see examples of them in action."
- },
"dependency-injection": {
"title": "Dependency Injection",
diff --git a/public/docs/ts/latest/guide/forms.jade b/public/docs/ts/latest/guide/forms.jade
index 22286dd88c..6aabb05601 100644
--- a/public/docs/ts/latest/guide/forms.jade
+++ b/public/docs/ts/latest/guide/forms.jade
@@ -1,464 +1,579 @@
include ../../../../_includes/_util-fns
+
+:markdown
+ We’ve all used a form to login, submit a help request, place an order, book a flight,
+ schedule a meeting and perform countless other data entry tasks.
+ Forms are the mainstay of business applications.
+
+ Any seasoned web developer can slap together an HTML form with all the right tags.
+ It's more challenging to create a cohesive data entry experience that guides the
+ user efficiently and effectively through the workflow behind the form.
+
+ *That* takes design skills that are, to be frank, well out of scope for this chapter.
+
+ It also takes framework support for
+ **two-way data binding, change tracking, validation, and error handling**
+ ... which we shall cover in this chapter on Angular forms.
+
+ We will build a simple form from scratch, one step at a time. Along the way we'll learn
+
+ - how to build an Angular form with a component and template
+
+ - the `ng-model` two-way data binding syntax for reading and writing values to input controls
+
+ - the `ng-control` directive to track the change state and validity of form controls
+
+ - the special CSS classes that `ng-control` adds to form controls and how we can use them to provide strong visual feedback
+
+ - how to display validation errors to users and enable/disable form controls
+
+ - how to share information across controls with template local variables
+
.l-main-section
+:markdown
+ ## Template-Driven Forms
+
+ Many of us will build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
+ the form-specific Directives and techniques described in this chapter.
+.l-sub-section
:markdown
- Forms play an important role in Web applications whether it’s collecting data or allowing data to be modified or deleted. We’ve all used a form to login to an application, submit a help request to a website, manipulate business data and more. While creating custom forms in HTML is a relatively straightforward process, the complexity increases when a form requires data binding, event handling, validation rules and error messages.
-
- Angular 2 provides several key features that can be used to build any type of form your application may need whether it’s a large enterprise line of business form or a small form used to log a user into an application. Throughout this chapter we’ll learn about the different approaches we can take when building forms and see examples of them in action.
-
- The overall learning goals of this chapter include:
-
- - Learn how to build an Angular 2 form component and template.
- - Learn data binding syntax that can be used to minimize the amount of code you write when building forms.
- - Learn about key Angular 2 directives that can be used in forms.
- - Learn how to display errors to users and enable/disable form controls.
- - Learn what template local variables are and how they can be used.
- - Learn about CSS classes that Angular 2 adds to form controls and understand how they can be used to provide visual feedback to users.
-
- Let’s get started by taking a look at the process we can follow to create template-driven forms in Angular 2.
-
- ## Introduction to Template-Driven Forms
-
- We can use template-driven forms to create login forms, contact forms and custom line of business forms in our Angular 2 applications. We can create any type of form layout, bind data to form controls, display validation errors, disable specific form controls when a form is invalid, provide visual feedback to users, plus more. In scenarios where we need to capture data from end users or allow them to modify existing data, template-driven forms provide a robust option that can be created quickly and easily using Angular 2 features.
-
- Here’s an example of a simple template-driven form in action. We’ll discuss the form throughout this chapter and learn how to build it.
-
- figure.image-display
- img(src="/resources/images/devguide/forms/tdf-1.png" alt="Clean Form")
-
- :markdown
- Here’s the same form displaying a validation error and providing additional visual feedback to the end user:
+ That's not the only way to create a form but it's the way we'll cover in this chapter.
+:markdown
+ We can build almost any form we need with an Angular template ... login forms, contact forms ... pretty much any business forms.
+ We can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
+ conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
+
+ It will be pretty easy because Angular handles many of the repeative, boiler plate tasks we'd
+ otherwise wrestle with ourselves.
+
+ We'll discuss and learn to build the following template-driven form:
+
+figure.image-display
+ img(src="/resources/images/devguide/forms/hf-1.png" alt="Clean Form")
+
+:markdown
+ Here at the *Hero Employment Agency* we use this form to maintain personal information about the
+ heroes in our stable. Every hero needs a job. It's our company mission to match the right hero with the right crisis!
- figure.image-display
- img(src="/resources/images/devguide/forms/tdf-2.png" alt="Invalid, Name Required")
-
- :markdown
- If the user doesn’t satisfy all of the validation requirements for the form, the submit button will automatically be disabled and the left portion of the input control that has an error will change from green to red. The colors used, location of the colors and more can be customized using standard CSS. Required controls have a red left-border to make them easy to visually identify for end users:
+ Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot.
+
+ If we delete the hero name, the form displays a validation error in an attention grabbing style:
- figure.image-display
- img(src="/resources/images/devguide/forms/tdf-3.png" alt="Invalid Form")
-
- :markdown
- What features does Angular 2 provide that we can use to create this type of template-driven form? We’ll answer this question and others by walking through the following steps:
+figure.image-display
+ img(src="/resources/images/devguide/forms/hf-2.png" alt="Invalid, Name Required")
- 1. Create an HTML form template
- 1. Create a model class
- 1. Create a form component
- 1. Add the **ng-submit** directive to the form
- 1. Add the **ng-control** directive to each form input control
- 1. Add the **ng-model** directive to each form input control
- 1. Disable the form’s submit button until the form is valid
- 1. Add custom CSS to provide visual feedback
- 1. Show and hide validation error messages
-
- The following sections provide a more detailed look at these steps and the code that we’ll need to write to make it all happen. Let’s start by looking at the initial form template.
-
- ### Create an HTML Form Template
-
- We can use a standard HTML form with the Angular template-driven approach. An example of a form template named **template-form.component.html** is shown next. The HTML content includes the form element and associated inputs as well as a section after the form that’s used to display data submitted by the end user.
+:markdown
+ Note that the submit button is disabled and the "required" bar to the left of the input control changed from green to red.
+
+.l-sub-section
+ p We'll' customize the colors and location of the "required" bar with standard CSS.
- code-example(format="linenums" language="html").
- <div class="container">
- <div [hidden]="submitted">
- <h1>Template Driven Form</h1>
- <form>
- <div class="form-group">
- <label for="name">Name</label>
- <input type="text" class="form-control" required>
- </div>
- <div class="form-group">
- <label for="alterEgo">Alter Ego</label>
- <input type="text" class="form-control" required>
- </div>
- <div class="form-group">
- <label for="power">Hero Power</label>
- <select class="form-control" required>
- <option *ng-for="#p of powers" [value]="p">{{p}}</option>
- </select>
- </div>
- <button type="submit" class="btn btn-default">Submit</button>
- </form>
- </div>
- <div [hidden]="!submitted">
- <h2>You submitted the following:</h2>
- <div class="row">
- <div class="col-md-2">Name</div>
- <div class="col-md-10 pull-left">{{ model.name }}</div>
- </div>
- <div class="row">
- <div class="col-md-2">Alter Ego</div>
- <div class="col-md-10 pull-left">
- {{ model.alterEgo }}
- </div>
- </div>
- <div class="row">
- <div class="col-md-2">Power</div>
- <div class="col-md-10 pull-left">
- {{ model.power }}
- </div>
- </div>
- <br />
- <button class="btn btn-default" (click)="submitted=false">Edit</button>
- </div>
- </div>
-
- :markdown
- The form captures Name, Alter Ego and Power values and makes all three of the inputs required by using the **required** attribute. The select control in the form iterates through an array of powers and displays them as select options using the Angular 2 **ng-for** directive.
-
- The application switches between the form view and the data display view by toggling a **submitted** property that we’ll discuss later in this chapter. The **submitted** property is bound to the **hidden** property of the div containers that wrap the form section and data display section.
+:markdown
+ We will build this form in the following sequence of small steps
- Now that the template for the form is in place, we can move on to discussing the model object that is bound to the form.
-
- ## Create a Model Class
-
- As users interacts with the form we’ll capture the data they enter or modify in instances of a “model” class. A model class is simply a class that holds application data. In this example **Hero** is the model class and it has **id**, **name**, **power**, and **alterEgo** properties.
-
- Here’s the complete code for the Hero model (hero.ts):
-
- ```
- export class Hero {
+ 1. Create the `Hero` model class
+ 1. Create the component that controls the form
+ 1. Create a template with the initial form layout
+ 1. Add the **ng-model** directive to each form input control
+ 1. Add the **ng-control** directive to each form input control
+ 1. Add custom CSS to provide visual feedback
+ 1. Show and hide validation error messages
+ 1. Handle form submission with **ng-submit**
+ 1. Disable the form’s submit button until the form is valid
+
+.l-main-section
+:markdown
+ ## Create the Hero Model Class
+
+ As users enter form data, we capture their changes and update an instance of a model.
+ We can't layout the form until we know what the model looks like.
+
+ 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`)
+ and one optional field (`alterEgo`).
+
+ Create a new file called `hero.ts` and give it the following class definition:
- constructor(
- public id?: number,
- public name?: string,
- public power?: string,
- public alterEgo?: string
- ) { }
-
- }
- ```
- The TypeScript compiler generates a public field for each **public** constructor parameter and assigns the parameter’s value to that field automatically. For example, TypeScript generates an **alterEgo** field to match the **public alterEgo** constructor parameter and sets the alterEgo field to the optional value of the fourth argument. This is a convenient way to initialize an object through the constructor with minimal code.
-
- Let’s create a form component that uses this **Hero** model class.
-
- ## Create a Form component
++makeExample('forms/ts/src/app/hero.ts')
- The HTML form template shown earlier is placed directly in an Angular component using the **View** annotation’s **template** property. Alternatively, we can create a separate file and link it to the component using the **View** annotation’s **templateUrl** property. Which approach is better? There’s no “right” answer since it’s very subjective, but in cases where a template contains a lot of HTML content, having a separate template file can be helpful and minimize the overall size of the component.
-
- In this example we’ll link the template to a component named **TemplateFormComponent (template-form.component.ts)** using the **templateUrl** property of **View** annotation. The complete code for **TemplateFormComponent** is shown next:
- ```
- import {Component, View, NgFor, FORM_DIRECTIVES} from 'angular2/angular2';
-
- import { Hero } from './hero';
-
- @Component({
- selector: 'template-driven-form',
- templateUrl: 'app/template-form.component.html',
- directives: [FORM_DIRECTIVES, NgFor]
- })
- export class TemplateFormComponent {
-
-
- model: Hero;
- powers: string[];
- submitted: boolean = false;
-
- constructor() {
- this.model = new Hero(18,'Dr IQ', 'Really Smart', 'Chuck Overstreet');
-
- this.powers = ['Really Smart', 'Super Flexible',
- 'Super Hot', 'Weather Changer'];
- }
-
- onSubmit() {
- this.submitted = true;
- }
-
- }
- ```
- We can see that there’s nothing unique about this component even though we’re using it to create a form. Many of the Angular 2 concepts we’ve already learned apply. Here are few of the key features shown in the component:
-
- 1. We import **NgFor, Component, View** and **FORM_DIRECTIVES** as well as the the **Hero** model class. Ng
- 1. We add the **Component** selector value of “template-driven-form”. We also define the form template **(template-form.component.html)** using the **templateUrl** property and all of the directives we’ll use in the form template using the **directives** property.
- 1. The **TemplateFormComponent** class **model, powers** and **submitted** properties defined.
- 1. The constructor initializes the model and powers properties with values.
-
- We’ve now linked the form template to the form component and integrated the **Hero** model class into the component. The next step is to integrate Angular 2 directives into the form.
-
- ## Add the ng-submit Directive to the Form
-
- We’ll now shift our attention back to the form template **(template-form.component.html)** created earlier. Although the form is linked to the component, it won’t do much at this point aside from showing the different form controls. Angular 2 directives have to be added into the form so that we can bind the Hero model and perform other tasks such as validation.
-
- To start, we can add the **ng-submit** event to the **form** element and hook it to the **onSubmit()** function shown earlier in the **TemplateFormComponent class**:
- ```
-