One of the things I noticed when I completed the official "Tour of Heroes" Angular 2 tutorial was that there wasn't a lesson on using forms: in Angular 1 input bindings that were managed under ngForm provided data state, validation, and error-handling features, and I had heard that Angular 2 had the same thing.

Apparently forms are another aspect of Angular 2 that is still somewhat of a moving target: the current "Forms" chapter under the "Basics" category of the Angular 2 site documents a deprecated version and points to a newer documentation page. I decided to read through the newer documentation page and try out the current forms functionality myself, building off of my existing Tour of Heroes project codebase.

The decision to use my Tour of Heroes code ended up causing a few problems. The first problem I ran into was that I didn't have an "@angular/forms" module to import the form providers from per the documentation instructions. It's not included in the package.json file used for both Tour of Heroes and the QuickStart. An execution of "npm view @angular2/forms" told me that the forms module current version was "0.2.0". I updated package.json, deleted my current node_modules folder, and ran "npm install", and after that I had an "@angular/forms" module folder, and I thought I was in business.

However, I got a 404 error trying to load "@angular/forms" when I tried to run the application. That one caused some head-scratching until I realized where I went wrong, Having been several weeks since I set up the Quick Start tutorial and then later having copied over those configuration files, I had forgotten about the role of the systemjs.config.js file. The array of ngPackageNames determines what packages under the "@angular" node_modules folder are loaded, and "forms" was not in the array. Once I added it, the error went away and I could actually focus on the exercises in the documentation.

The biggest takeaway from the page was that using the [{ngModel}] binding on a form control leads to that form control being decorated with CSS classes that describe the state of the form control:

ng-untouched vs. ng-touched, which indicate if the user has interacted with the form control via the mouse or keyboard.

ng-pristine vs. ng-dirty, which indicate whether the value of the form control has changed.

ng-valid vs. ng-invalid, which indicates if the value of the form control is valid or invalid.

There are some nuances to those explanations, some of which were explained on the documentation page and some that I determined for myself:

The class change from untouched to touched doesn't take place until the form control loses focus after the user has touched it (put the form control in focused stated) with either the mouse of keyboard.

The class change from pristine to dirty only takes place if the user changes the value of the form control via the UI, such as by typing or by performing a paste action in the form control. Changing the value of the model data bound to the input programmatically does not trigger the change from pristine to dirty.

The untouched-to-touched and pristine-to-dirty transitions are one-way transitions. If you delete the last letter of the value in a text input and then restore it (so the value is the same as it was when the text input DOM element was created on the page), the text input is still labeled with ng-dirty. The document emphasizes this point with an example of how to "reset" the pristine state of the form controls by destroying and recreating the form using ngIf and a conditional, and hints that a proper "form reset" action may be forthcoming. There is a GitHub issue on the topic: https://github.com/angular/angular/issues/4933. Having had some programmatic experience with resetting form values, I'm interested to see how they solve the reset issue.

The documentation page demonstrates the transition of a text input control from validity to invalidity in conjunction with the use of the "required" attribute on the <input> element. But it currently doesn't explain how Angular determines the validity of the form control value under other circumstances. Angular didn't mark the input as invalid when I entered a non-URL value in an input with the HTML5-supported type of "URL", nor did it react when I entered a value in another text input that exceeded the value of the "min" attribute. A search through the Angular.io site didn't turn up any page that clearly explained how to perform the validation with the latest implementation of forms, though the references to Validators and their use implies that the answer likely involves applying validation rules or logic programmatically. The documentation page concluded with code exercises that demonstrated how the form as a whole has a validity state property (courtesy of the ngForm directive that is quietly attached to the <form> tag automatically) which is affected by the validity/invalidity of the individual form control values, and then how the form validity property can be used to block the submission of the form if it's currently invalid.

After going through this page, I can see why forms were not covered as a topic in the Tour of Heroes tutorial. The implementation of form-related behavior in Angular 2 is still evolving, and though this documentation page illustrates some of the expected behavior and benefits it does so at a basic and somewhat vague level. I'll have to revisit this topic down the road after the documentation is more fleshed out.