Chapter 9. Forms

Forms are an essential part to all business applications. They are used to login, to submit requests, to place orders, and they have countless other uses. Developers need to pay attention to forms because they modify data, provide for validation, and they display the changes that are reflected on the page.

As a web developer, you must effectively use the correct HTML tags for the form to be displayed to the user. The real challenge is to create an optimal data entry experience that will guide the user efficiently and effectively through the workflow behind the form.

Fortunately, Angular 2 developers took the time to really integrate new tools for developers to create methodical forms through controls, validators, and observers. Controls are what encapsulates the inputs in our forms and gives us the objects to work with. Validators allow the ability to validate inputs to our own discretion. Finally, observers let us watch the form change and respond.

Controls

Two fundamentals developers you need to pay attention to in the ng2 form are the Control and ControlGroup. Since controls represent a single input field, it is considered the smallest unit of an Angular form. This is important to remember, because it encapsulates the value the field gave us and the state if the field is valid or not. We use Controls to build up the forms we create and attach metadata and logic to them.

For example, you can create a Control in TypeScript like this:

let exampleControl = new Control (Jane);
let name = exampleControl.value;

exampleControl.errors
exampleControl.dirty
exampleControl.valid

We need to remember that in Angular 2, you have to attach the Control class to the DOM. Make sure to include the ngControl attribute. This creates a new Control object within the context of our form.

<input type=text ngControl=name />

ControlGroup

In the previous example, our form had only one field. Angular 2 has developed ControlGroup to manage multiple controls. Developers use ControlGroups because it becomes inconvenient to iterate over an array of Controls and check to see if they are valid. ControlGroups have a wrapper interface around the collection of Controls for better management and organization of forms.

let exampleInfo = new ControlGroup ({
    firstName: new Control(Jane),
    lastName: new Control(Doe)
})

You’ll notice that when you try to get the value from a ControlGroup, you’ll receive an object with key-value pairs. As web developers, this is appreciated because you do not have to iterate each Control to get the full set of values from the form.

exampleInfo = exampleInfo.value;

exampleInfo.errors
exampleInfo.dirty
exampleInfo.valid

Your First Form

Lets create a hypothetical form that will store a product name. We will have one input and a submit button. We will also turn our form into a component to make it available throughout the application.

The entire application is openly available at https://github.com/flauc/angular2-edge-app.

The Component

import { Component } from ‘@angular/core;
import { FORM_DIRECTIVES} from ‘@angular/common;

@Component({
    selector: product-form,
    directives: [FORM_DIRECTIVES],
})

In this component, we have defined the selector as product-form in order to call it into our HTML, where it should look like this:

<product-form></product-form>

Take special note of FORM_DIRECTIVES. FORM_DIRECTIVES is a special constant that Angular provides as shorthand to several directives that are useful in forms.

The Template

Now let’s add our form template to the component.

import { Component } from ‘@angular/core;
import { FORM_DIRECTIVES} from ‘@angular/common;

@Component({
    selector: product-form,
    directives: [FORM_DIRECTIVES],
    template: 
    <div class=form-group>
        <h2 class=header> Product Name Form</h2>
        <form #f=ngForm
            (ngSubmit)=onSubmit(f.value) class=form>

    <div class=field>
        <label for=productInput> Product Name</label?
   <input type=text 
    id=productInput 
    placeholder=Product Name     
    ngControl=product>
    </div>

    <button type=submit class=button>Submit</button>
    </form>
</div>

})

By using FORM_DIRECTIVES, we opened a huge door for many different kinds of selectors to be used. One of the most important, selectors, forms and requires ngForm as an attribute. This is significant because ngForm automatically attaches to any form tag in that view.

By having #f=”ngForm” in our template, we are creating a local variable for this view. This is happening because we are creating an alias to find to the #f variable. ngForm is a ControlGroup object because we can use f as a ControlGroup in our view.

The input tags are more tied to CSS frameworks and are not really associated with Angular.

ngControl is another significant directive that we need to pay attention to because it specifies a selector of ngControl. It is important because we can attach our input tag by adding an attribute to it. We used ngControl=”product”. NgControl will eventually add a new Control to the parent ControlGroup, and will bind a DOM element to the new Control.

Using FormBuilder

When we build our forms using Controls and ControlGroups with ngForm and ngConrol, it doesn’t allow us many options for customizing. However, if you use FormBuilder, you have more flexibility to customize your form.

FormBuilder is a helper class that helps build forms. Let’s build a form with FormBuilder.

First, we create our component:

export class ProductNameBuilder {
    form: ControlGroup;

    constructor(fb: FormBuilder) {
         this.form = fb.group({
   name:[ExampleProduct]
 });
}

    onSubmit(value: string): void {
    console.log(The product name you submitted: , value);
 }
}

An instance of FormBuilder is created during injection and is assigned an fb variable in the constructor. Our variable is called form. form is typed in a ControlGroup. Our ControlGroup is called fb.group() and takes an object of key-value pair that specifically target the Controls.

Formbuilder in Your Views

In order to use FormBuilder in our template, we must call the correct tag in HTML. Since our instance variable is form, we will use the HTML tags <form></form>. However, we need to use ngFormModel because ngFormModel creates its own existing ControlGroup, and we do not want to go outside of it.

It should look similar to this:

 form [ngFormModel]=form
 (ngSubmit)=onSubmit(form.value)

Lastly, we need to bind our Control to input the tag. Since ngControl creates a new object and will attach the parent ControlGroup, we need to use FormBuilder to create our own Controls to bind with the input tag. In order to do this, we need to bind the existing Control to NgFormControl. NgFormControl instructs the directive to look at form.controls and to use the existing product Control for this input.

<input type=text 
 id=productInput 
 placeholder=Product Name
 [ngFormControl]=form.controls[product]>

Validators

Some users aren’t always going to enter the correct information into the input fields. The solution for this is using validations to provide feedback to the user and not have the form be submitted.

Validators are provided by the Validators module. The most simple validator is Validators.required, which is designed for a required field or the Control will be considered invalid. We need to remember that in order to use validators, we need to assign the validator to a Control object and check the status of the validators through the view.

In order to assign a validator to a Control object, we pass it as a second argument to our Control constructor.

let control = new Control(‘product’, Validators.required);

However, if you are using FormBuilder, you would use this syntax:

constructor(fb: FormBuilder) {
    this.form = fb.group({
        product: [  ,  Validators.required]
})
}

After the validator is written, we need to use the validation in the view by

  1. Specifically assigning the Control product to an instance variable to give you easy access to the view. Or
  2. Lookup the Control product from form in the view.

In order to add the Control product to an instance variable, we set each Control up as an instance variable in your component definition class.

export class FormValidation {
    form: ControlGroup;
    product: ExampleControl;
 constructor(fb: FormBuilder) {
 this.form = fb.group({
    product: [ , Validators.required]
});

this.product = this.form.controls[product];
}

 onSubmit(value: string): void {
  console.log (The product name you submitted: , value);
}

Since the product is being validated, we now want to look at our view checking for the forms entire validation, the individual field, and displaying message validation. We also check the coloring of the field if it’s invalid, check the validity of the individual field on a certain requirement, and display the message.

Form Messages

To check the validation of the entire form, you can specifically look at form.valid:

div *ngIF= !form.valid

Since form is a ControlGroup, the ControlGroup is valid when the children Controls are valid.

Field Message

Similarly, we can display a message for a specific field if the field’s Control is invlaid.

    div *ngIF= !product.valid

Field Coloring

If you add the Semantic UI CSS Framework into your application, you can have it do most of the work for you. The UI CSS Framework adds a .error class to <div class= “field”>.

<div class=field
    [class.error]=!product.valid && sku.touched>

Specific Validation

Forms can be invalid for different reasons, so it is important to be able to show different messages to indicate the reason the form is invalid. You can look up a validation failure by using the hasError method.

div *ngIf=product.hasError(required)>

Remember that hasError is defined in the Control and the ControlGroup. This means that you can pass another argument to lookup a specific field in the ControlGroup.

Creating the product Control as an instance variable without creating an instance for every Control

Instead of creating an instance variable for every Control, we can create a productreference. You can approach making a reference in two different ways:

1) Through form.find. 2) Through the ngFormControl directive.

Field Coloring

ControlGroup has a .find method that allows you to look up a child Control by a path.

div class=”field”
    [class.error]=”!form.find(‘product’).valid && form.find(product’).touched”>

If you compare the syntax from the previous field coloring, to this syntax, you can see that it is more concentrated with more methods attached to it.

Form Reference Through Form Export

As previously stated, you can get a reference to the Control through the ngForm explort of the NgFormControl directive. Since components can export a reference, you can reference the form there to use in your view.

<input type=text 
id=productInput 
placeholder=Product Name
#product=ngForm 
[ngFormControl]=Form.controls[product]>

However, by referencing the NfFormControl directive with #product=”ngForm”, the product becomes an instances of the directive and is NOT a Control. In order to get the reference to the Control, you would need to call product.control.

Since product is available to us as a reference, we can now check for errors, and if the input validates by using the following syntax:

<div *ngIF=!product.control.valid class=error message>Product is not valid</div>
<div *ngIf=product.control.hasError(required) class=error message> Product is required</div>

Custom Validations

Often times, we want to write our own validations. In order to understand how our validators are implemented, its important to understand the class, Validators.required from Angular’s core.

A validator will take a Control as its input and return a StringMap<string, Boolean> where the key is “error code”. When that value is true, it fails.

Writing a Custom Validator

In our forms, we can assign specific requirements to our product. In our example, let’s give our product a requirement that it needs to be assigned a KU_ in front of every product name. The validator would be written similar to this:

function productValidator(control: Control): { [s: string]: boolean] } {
    if (!control.value.match(/^KU_/))  {
        return { invalidProduct: true};
}
}

But now this validator needs to find a home in our Control. Since we already have a validator in our product, we need to use Validators.compose.

This.form = fb.group({
    product: [ , Validators.compose([
Validators.required. productValidator])]
});

When we use Validators.compose, it wraps multiple validators and assigns them to the Control. This lets the Control be valid when both validations pass.

In our view, we would use our new validator like this:

div *ngIf=”product.hasError(‘invalidProduct’)”
    class=”error message”> Product name must begin with a KU_</div>

ngModel

ngModel is a special directive that we use in our forms because it will bind a model to a form. But what makes ngModel particularly significant is that it can implement two-way data binding. Two-way data can be rather difficult at times, but it can be necessary in some situations.

If we were to add another input field to our form, we can use ngModel to keep the component instance variable in line with the view.

The component class can look similar to this:

export class ExampleFormModel {
    form: ControlGroup;
    productDescription: string;

constructor(fb: FormBuilder) {
    this.Form = fb.group({
        productDescription: [ , Validators.required]
    });
}

onSubmit(value: string): void {
    console.log(You submitted a product description:  value);
}
}

Now, lets put ngModel in the input tag:

<input type=text
    id=productDescription
    placeholder= Product Description
    [[ngFormControl]=form.find(productDescription)
    [(ngModel)]=productDescription>
`

By using brackets and parenthesis around ngModel, it indicates two-way binding.

Lastly, lets display productDescription within our view:

<div class=product description>
    The product description is: {{productDescription}}
</div>

Summary

Forms are one of the biggest assets a developer can use for data collection in any type of business web application. It is up to the developer to decide the best and most effective approach to design and use the form. This is important to remember when implementing a form in an application, and to consider the usability and experience the user will have.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.17.40