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 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! 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/hf-2.png" alt="Invalid, Name Required") :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. :markdown We will build this form in the following sequence of small steps 1. Create the `Hero` model class 1. Create the component that controls the form 1. Create a template with the initial form layout 1. 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 :markdown ## Setup Create a new project folder (`angular2-forms`) and follow the steps in the [QuickStart](../quickstart.html). ## 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: +makeExample('forms/ts/src/app/hero.ts') :markdown It's an anemic model with few requirements and no behavior. Perfect for our demo. The TypeScript compiler generates a public field for each `public` constructor parameter and assigns the parameter’s value to that field automatically when we create new heroes like this: ``` let myHero = new Hero(42, 'SkyDog', 'Fetch any object at any distance', 'Leslie Rollover'); console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog" ``` The `alterEgo` is optional and the constructor lets us omit it; note the (?) in `alterEgo?`. .l-main-section :markdown ## Create a Form component An Angular form has two parts: an HTML-based template and a code-based Component to handle data and user interactions. We begin with the Component because it states, in brief, what the Hero editor can do. Create a new file called `hero-form.component.ts` and give it the following definition: +makeExample('forms/ts/src/app/hero-form.component.ts', 'first') :markdown There’s nothing special about this component, nothing to distinguish it from any component we've written before, nothing form-specific about it ... except, perhaps, the tell-tale `FORM_DIRECTIVES` import. Understanding this component requires only the Angular 2 concepts we’ve learned in previous chapters 1. We import a standard set of symbols from the Angular library. We don't have a template yet but we usually import `CORE_DIRECTIVES` and it doesn't surprise us to import something called `FORM_DIRECTIVES`, given that we'll be writing a form 1. The `@Component` selector value of "hero-form" means we can drop this form in a parent template with a `` tag. 1. The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`. 1. We defined dummy data for `model` and `powers` as befits a demo. Down the road, we can inject a data service to get and save real data or perhaps expose these properties as [inputs and outputs](./template-syntax.html#inputs-outputs) for binding to a parent component. None of this concerns us now and these future changes won't affect our form. 1. We threw in a `diagnostic` property at the end to return a JSON representation of our model. It'll help us see what we're doing during our development; we've left ourselves a cleanup note to discard it later. We may wonder why we aren't writing the template inline in the component file as we have often done elsewhere in the Developer Guide. There is no “right” answer for all occasions. We kind of like inline templates when they are short. Most form templates won't be short. TypeScript and JavaScript files generally aren't the best place to write (or read) large stretches of HTML and few editors are much help with files that have a mix of HTML and code. We also like short files with a clear and obvious purpose like this one. We made a good choice to put the HTML template elsewhere. Let's write it. .l-main-section :markdown ## Revise the *app.ts* `app.ts` is the application's root component. It will host our new `HeroFormComponent`. Replace the contents of the "QuickStart" version with the following: +makeExample('forms/ts/src/app/app.ts') :markdown .l-sub-section :markdown There are only three changes: 1. We import the new `HeroFormComponent`. 1. The `template` is simply the new element tag identified by the component's `select` property. 1. The `directives` array tells Angular that our templated depends upon the `HeroFormComponent` which is itself a Directive (as are all Components). .l-main-section :markdown ## Create an initial HTML Form Template Create a new template file called `hero-form.component.html` and give it the following definition: +makeExample('forms/ts/src/app/hero-form.component.html', 'start') :markdown That is plain old HTML 5. We're presenting two of the `Hero` fields, `name` and `alterEgo`, and opening them up for user input in input boxes. The "Name" `` control has the HTML5 `required` attribute; the "Alter Ego" `` control does not because `alterEgo` is optional. We've got a "Submit" button at the bottom with some classes on it. **We are not using Angular yet**. There are no bindings. No extra directives. Just layout. The `container`,`form-group`, `form-control`, and `btn` classes are CSS Bootstrap. Purely cosmetic. We're using Bootstrap to gussy up our form. Hey, what's a form without a little style! .l-sub-section :markdown Since we're using [CSS Boostrap](http://getbootstrap.com/css/). now might be a good time to install it into our project. We can do that with npm. Open a terminal window and enter the command: code-example(language="html" escape="html"). npm install bootstrap :markdown
Open the `index.html` and add the following line wherever we like to put our CSS +makeExample('forms/ts/src/index.html', 'bootstrap')(format=".") .callout.is-important header Angular Forms Does Not Require A Style Library :markdown Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or the styles of any external library. We are welcome to use the CSS library we choose ... or none at all. .l-main-section :markdown ## Add Powers with ***ng-for** Our hero may choose one super power from a fixed list of Agency-approved powers. We maintain that list internally (in `HeroFormComponent`). We'll add a `select` to our form and bind the options to the `powers` list using `NgFor`, a technique we might have seen before in the ["Displaying Data"](./displaying-data.html) chapter. Add the following HTML *immediately below* the "Alter Ego" group. +makeExample('forms/ts/src/app/hero-form.component.html', 'powers') :markdown We are repeating the `` tag for each power in the list of Powers. The `#p` local template variable is a different power in each iteration; we display its name using the interpolation syntax with the double-curly-braces. .l-main-section :markdown ## Two-way data binding with ***ng-model** We might be disappointed if we ran the app right now. figure.image-display img(src="/resources/images/devguide/forms/hf-3.png" alt="Early form with no binding") :markdown We quickly realize that we are not binding to the `Hero` yet. We know how to do that from earlier chapters. We learned show data on screen with a Property Binding in "[Displaying Data](./displaying-data.html)". We learned to listen for DOM events with an Event Binding and how to extract values from the screen in "[User Input](./user-input.html)". Now we need to display, listen, and extract at the same time. We could use those techniques again in our form. Instead we'll introduce something new, the `NgModel` directive, that makes binding our form to the model super-easy. Find the `` tag for the "Name" and update it like this +makeExample('forms/ts/src/app/hero-form.component.html', 'ng-model-1') .l-sub-section :markdown We appended a diagnostic interpolation after the input tag so we can see what we're doing. We left ourselves a note to throw it way when we're done. :markdown Focus on the binding syntax: `[(ng-model)]="..."`. If we ran the app right now and started typing in the "Name" input box, adding and deleting characters, we'd see them appearing and disappearing from the interpolated text. At some point it might look like this. figure.image-display img(src="/resources/images/devguide/forms/ng-model-in-action.png" alt="ng-model in action") :markdown The diagnostic is evidence that we really are flowing values from the input box to the model and back again. **That's two-way data binding!** Let's add similar `[(ng-model)]` bindings to "Alter Ego" and "Hero Power". We'll ditch the input box binding message and add a new binding at the top to the component's `diagnostic` property. Then we can confirm that we are in fact two-way data binding *to the entire Hero model*. After revision the core of our form should have three `[(ng-model)]` bindings that look much like this: +makeExample('forms/ts/src/app/hero-form.component.html', 'ng-model-2') :markdown If we ran the app right now and made a bunch of changes at some point it might look like this. figure.image-display img(src="/resources/images/devguide/forms/ng-model-in-action-2.png" alt="ng-model in super action") :markdown We've changed every Hero model property and the diagnostic near the top of the form confirms that our changes are reflected in the model. ** We're done with the diagnostic binding. Delete it now.** .alert.is-helpful :markdown Although `NgModel` is officially a "Forms" directive we can use `[(ng-model)]` and two-way binding outside of forms too. :markdown ## Inside [(ng-model)] Do we *really want* to know? If we're just happy that it works, move on to the next topic in this chapter. Otherwise, stick around for this note. .l-sub-section :markdown The punctuation in the binding syntax, [()], is a good clue to what's going on. We write a Property Binding to flow data from the model to a target property on screen. We identify that target property by surrounding its name in brackets, []. This is a one-way data binding **from the model to the view**. We write an Event Binding to flow data from the target property on screen to the model. We identify that target property by surrounding its name in parentheses, (). This is a one-way data binding in the opposite direction **from the view to the model**. No wonder Angular chose to combine the punctuation as [()] to signify a two-way data binding and a **flow of data in both directions**. In fact, we can break the `NgModel` binding into its two separate modes as we do in this re-write of the "Name" `` binding: +makeExample('forms/ts/src/app/hero-form.component.html', 'ng-model-3') :markdown
The Property Binding should feel familiar. The Event Binding might seem strange. The name `ng-model-change` is not an event we recognize. It is a real event property ... of the `NgModel` directive. When Angular sees a binding target in the form [(abc)], it expects the `abc` directive to have an `abc` input property and an `abc-change` output property. The other oddity is the template expression, `model.name = $event`. We're used to seeing an `$event` object coming from a DOM event. The `ng-model-change` property doesn't produce a DOM event; it's an Angular `EventEmitter` property that returns the input box value when it fires ... which is precisely what we should assign to the model's `name' property. Nice to know but is it practical? We'd always prefer the `[(ng-model)]`. We might split the binding if we had to do something special in the event handling such as debounce or throttle the key strokes. Learn more about `NgModel` and other template syntax in the [Template Syntax](./template-syntax.html) chapter. .l-main-section :markdown ## Track change-state and validity with **ng-control** A form isn't just about data binding. We'd also like to know the state of the controls on our form. The `NgControl` directive keeps track of control state for us. .callout.is-helpful header NgControl requires Form :markdown The `NgControl` is one of a family of `NgForm` directives that can only be applied to a control within a ` tag. :markdown Our application can ask an `NgControl` instance if * the user touched the control (`ng-touched` | `ng-untouched`) * the value changed (`ng-pristine` | `ng-dirty`) * is the value is valid (`ng-valid` | `ng-invalid`) `NgControl` doesn't just track state; it updates the control with special Angular CSS classes from the set we listed above. We can leverage those class names to change the appearance of the control and make messages appear or disappear. We'll explore those effects soon. Right now we should **add `ng-control`to all three of our form controls**, starting with the "Name" input box +makeExample('forms/ts/src/app/hero-form.component.html', 'ng-control-1') :markdown Be sure to assign a unique name to each `ng-control` directive. .l-sub-section :markdown Angular registers controls under their `ng-control` names with the `NgForm`. We didn't add the `NgForm` directive explicitly but it's here and we'll talk it [later in this chapter](#ng-form). .l-main-section :markdown ## Add Custom CSS for Visual Feedback `NgControl` doesn't just track state. It updates the control with three classes, one each from the following pairs of Angular form CSS classes. * control visited: (`ng-touched` | `ng-untouched`) * value changed: (`ng-pristine` | `ng-dirty`) * validity: (`ng-valid` | `ng-invalid`) Let's add a temporary [local template variable](./template-syntax.html#local-vars) named **spy** to the "Name" `` tag and use the spy to display those classes with an interpolation binding. +makeExample('forms/ts/src/app/hero-form.component.html', 'ng-control-2') :markdown If we ran the app, focused our attention on the "Name" input box, and followed the next four steps *precisely* 1. Look but don't touched 1. Click in the input box, then click outside the text input box 1. Add slashes to the end of the name 1. Erase the name ... we would see the following four sets of class names and their transitions: figure.image-display img(src="/resources/images/devguide/forms/ng-control-class-changes.png" alt="Invalid Form") :markdown The (`ng-valid` | `ng-invalid`) pair are most interesting to us. We want to send a strong visual signal when the data are invalid and we want to mark required fields. We realize we can do both at the same time with a colored bar on the left of the input box: figure.image-display img(src="/resources/images/devguide/forms/validity-required-indicator.png" alt="Invalid Form") :markdown We achieve this effect by adding two styles to a new `styles.css` file that we add to our project as a sibling to `index.html`. +makeExample('forms/ts/src/styles.css') :markdown These styles select for the two Angular validity classes and the HTML 5 "required" attribute. We update the `` of the `index.html` to include this style sheet. +makeExample('forms/ts/src/index.html', 'styles')(format=".") :markdown ## Show and Hide Validation Error messages We can do better. The "Name" input box is required. Clearing it turns the bar red. That says *something* is wrong but we don't know *what* is wrong or what to do about it. We can leverage the `ng-invalid` class to reveal a helpful message. Here's the way it should look when the user deletes the name: figure.image-display img(src="/resources/images/devguide/forms/name-required-error.png" alt="Name required") :markdown To achieve this effect we extend the `` tag with 1. a [local template variable](./template-syntax.html#local-vars) 1. the "*is required*" message in a nearby `
` which we'll display only if the control is invalid. Here's how we do it for the "name" input box: +makeExample('forms/ts/src/app/hero-form.component.html', 'name-with-error-msg') :markdown We initialized the template local variable with the word "form" (`#name="form"`) Angular recognizes that syntax and sets the `name` varable to the `Control` object identified by the `ng-control` directive which, not coincidentally, we called "name". We bind the `Control` object's `valid` property to the element's `hidden` property. While the control is valid, the message is hidden; if it becomes invalid, the message is revealed. .l-sub-section :markdown Recall from the previous section that `ng-control` registered this input box with the `NgForm` directive as "name". We didn't add the **[`NgForm`](../api/core/NgForm-class.html) directive** explicitly. Angular added it surreptiously, wrapping it around the `
` element when we told the `HeroFormComponent` to use the `FORM_DIRECTIVES` like this +makeExample('forms/ts/src/app/hero-form.component.ts', 'directives')
:markdown The `NgForm` directive supplements the the `form` element with additional features. It collects `Controls` (elements identified by an `ng-control` directive) and monitors their properties including their validity. It has its own `valid` property which is true only if every contained control is valid. In this example, we are pulling the "name" control out of its `controls` collection and assigning it to the template local variable so that we can access the control's properties ... such as the control's own `valid` property. :markdown The "AlterEgo" is optional so we can leave that be. "Power" selection is required. We can add the same kind of error handling to the `