Page 16 - MSDN Magazine, March 2018
P. 16

The Working Programmer TED NEWARD How To Be MEAN: Validating Angular
Welcome back again, MEANers.
In the previous column, I started looking at Angular’s support for
forms and input. Two-way binding took top billing, but any sense of how to validate input—to make sure that Speakers have both a first and a last name, for example, and not just empty strings—was left behind. In this month’s column, we need to rectify that, because data input without validation is basically just asking users to pour garbage into your system, leaving it to you to sort out.
And that, as most of us know, would be bad. Like, trademarked levels of Really, Really BadTM.
SpeakerUI, Redux
In the last column, I ditched the SpeakerEdit component in favor of SpeakerUI, which wraps around a Speaker model instance and knows—based on what’s passed into it—whether it’s in a read-only state for viewing the Speaker, or an editable state. It uses two <div> sections (one of which is hidden depending on the state the com- ponent is in) to keep the UI distinct (see Figure 1). The read-only section requires no validation, of course, because there’s no user input; it’s the editable section that concerns us here.
The first thing to note is that between last month’s column and this, I moved the logic for determining whether or not the edit mode can be canceled (notice how the Cancel button’s disabled property is bound) to a method on the component itself. This may be a bit of overkill in this particular case, but it does demonstrate an important aspect of Angular—that you do not have to do all of the UI logic directly inside the template itself. Should the cancella- tion logic get complicated, having that in the template is probably a bad idea, but we need to have the form object (the speakerForm object defined last month) available to use in the component code.
That requires the use of a new module, one that isn’t already present in the component: NgForm.
NgForm
NgForm is a class defined in Angular specifically for working with forms. It’s contained in a separate module from the rest of the Angular core, so it requires a standalone import to retrieve:
import { NgForm } from '@angular/forms';
When working with forms at runtime, Angular constructs a col- lection of objects that represents the various controls and the form itself, and uses it to do validation and other processing. This object collection is often hidden behind the scenes for convenience, but is always available to Angular developers for use.
Once passed into the cancellable method, you can use the form object to examine the state of the form via a number of properties. NgForm defines dirty, invalid, pristine, touched, untouched and valid properties to represent an entire spectrum of different user- interaction states with the form. For demonstration purposes, I’ll add a few more diagnostic lines to the editable section of the form:
<br>Pristine: {{speakerForm.form.pristine}} Dirty: {{speakerForm.form.dirty}}
Touched: {{speakerForm.form.touched}} Untouched: {{speakerForm.form.untouched}} Invalid: {{speakerForm.form.invalid}} Valid: {{speakerForm.form.valid}}
These will simply display the state of each of these as the user interacts with the form, and help explain what each represents. For example, “untouched” means—quite literally—the user hasn’t touched the form in any way. Simply clicking (or touching, on a mobile device) the edit field so that the cursor appears there is enough to render the form as being “touched.” However, if no typing has taken place, even if the form is “touched,” it’s still “pristine.” And so on.
Validity, as might be expected, suggests that the user has vio- lated some kind of data-entry constraint that the developer has mandated. Angular looks to build off of standard HTML5 validity constraints, so, for example, if you decide that speakers must have both a first and a last name, you can simply use the “required” attribute on the edit fields:
FirstName: <input name="firstName" type="text" [(ngModel)]="model.firstName" required><br> LastName: <input name="lastName" type="text"
[(ngModel)]="model.lastName" required><br>
Given this, if the user edits an existing Speaker and clears either the firstName or lastName edit field completely, the invalid state flips to true and the valid state to false, because Angular recognizes that the required flag is present. That said, though, Angular doesn’t do
Figure 1 The SpeakerUI Component
<form #speakerForm="ngForm"> <div [hidden]="readonly">
FirstName: <input name="firstName" type="text" [(ngModel)]="model.firstName"><br>
LastName: <input name="lastName" type="text" [(ngModel)]="model.lastName"><br> Subjects: {{model.subjects}}<br>
<button (click)="save()" [disabled]="!speakerForm.form.dirty">Save</button>
<button (click)="cancel()" [disabled]="!cancellable(speakerForm)">Cancel</button>
<br><span>{{diagnostic}}</span> </div>
... </form>


































































































   14   15   16   17   18