How to Deal with Different Form Controls in Angular 2

Jecelyn Yeen
๐Ÿ‘๏ธ 271,619 views
๐Ÿ’ฌ comments

In this tutorial we will explore the way to bind these few types of controls to our form:

  1. text
  2. number
  3. radio
  4. select (primitive type)
  5. select (object)
  6. multiple select
  7. checkbox (boolean)
  8. checkbox (toggle value)

Feel free to skip some of the control types (as some of them are really simple).

Related Video Course: Angular v2+ Forms and Validation

If you are new to Angular 2 forms, do refer to these articles for basics.

Table of Contents

    Introduction

    We will build a form to capture user information based on these interfaces.

    // user.interface.ts
    
    export interface User {
        name: string; // text
        age?: number; // number
        gender?: string; // radio
        role?: string; // select (primitive)
        theme?: Theme; // select (object)
        topics?: string[]; // multiple select 
        isActive?: boolean; // checkbox
        toggle?: string; // checkbox toggle either 'toggled' or 'untoggled'
    }
    
    // theme.interface.ts
    
    export interface Theme {
        display: string;
        backgroundColor: string;
        fontColor: string;
    }

    Here is how the UI will look:

    Angular 2 Form Controls

    App Setup

    Here's our file structure:

    |- app/
        |- app.component.html
        |- app.component.ts
        |- app.module.ts
        |- main.ts
        |- theme.interface.ts
        |- user.interface.ts
    |- index.html
    |- styles.css
    |- tsconfig.json

    In order to use forms module, we need to npm install @angular/forms npm package and import the forms module in application module.

    $ npm install @angular/forms --save

    Here's the module for our application app.module.ts:

    // app.module.ts
    
    import { NgModule }      from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { FormsModule } from '@angular/forms';
    
    import { AppComponent }   from './app.component';
    
    @NgModule({
      imports:      [ BrowserModule, FormsModule ], // import forms module
      declarations: [ AppComponent ],
      bootstrap:    [ AppComponent ]
    })
    
    export class AppModule { }

    The App Component

    Let's move on to create our app component.

    // app.component.ts
    
    import { Component, OnInit } from '@angular/core';
    
    import { User } from './user.interface';
    import { Theme } from './theme.interface';
    
    @Component({
        moduleId: module.id,
        selector: 'my-app',
        templateUrl: 'app.component.html',
        directives: []
    })
    export class AppComponent implements OnInit {
        public user: User;
        /* standing data goes here*/
        ...
        /* end standing data */
        ngOnInit() {
            // initialize user model here
        }
        public save(isValid: boolean, f: User) {
            console.log(f);
        }
    }

    Standing Data

    We need to include some data for setup as well:-

    // app.component.ts
    ...
    
    /* standing data goes here*/
    public genders = [
        { value: 'F', display: 'Female' },
        { value: 'M', display: 'Male' }
    ];
    public roles = [
        { value: 'admin', display: 'Administrator' },
        { value: 'guest', display: 'Guest' },
        { value: 'custom', display: 'Custom' }
    ];
    public themes: Theme[] = [
        { backgroundColor: 'black', fontColor: 'white', display: 'Dark' },
        { backgroundColor: 'white', fontColor: 'black', display: 'Light' },
        { backgroundColor: 'grey', fontColor: 'white', display: 'Sleek' }
    ];
    public topics = [
        { value: 'game', display: 'Gaming' },
        { value: 'tech', display: 'Technology' },
        { value: 'life', display: 'Lifestyle' },
    ];
    public toggles = [
        { value: 'toggled', display: 'Toggled' },
        { value: 'untoggled', display: 'UnToggled' },
    ];
    /* end standing data */
    
    ...

    Initialize model

    Then, we need to initialize our user model:-

    // app.component.ts
    ...
    
    ngOnInit() {
        // initialize user model here
        this.user = {
            name: '',
            gender: this.genders[0].value, // default to Female
            role: null,
            theme: this.themes[0], // default to dark theme
            isActive: false,
            toggle: this.toggles[1].value, // default to untoggled
            topics: [this.topics[1].value] // default to Technology
        }
    }
    ...

    The HTML View

    This is how our HTML view will look like.

    <!-- app.component.html -->
    
    <form #f="ngForm" novalidate>
    
        <!-- We'll add our form controls here -->
    
        <button type="submit" (click)="save(f.value, f.valid)">Submit</button>
    </form>

    Implementation

    Let's start to look into each type of controls.

    1. Text

    Getting text input is very straight forward. You need the name attribute, and ngModel.

    <!-- app.component.html -->
    ...
    <div>
        <label>Name</label>
        <input type="text" name="name" [(ngModel)]="user.name">
    </div>
    
    ...

    2. Number

    Getting number input is also very straight forward.

    <!-- app.component.html -->
    ...
    <div>
        <label>Age</label>
        <input type="number" name="age" [(ngModel)]="user.age">
    </div>
    
    ...

    3. Radio

    Binding radio input is not that easy prior Angular RC 2. With the new form in RC 3 onward, we can directly bind to ngModel, bind the value property.

    We have this list of genders:-

    // app.component.ts
    
    public genders = [
        { value: 'F', display: 'Female' },
        { value: 'M', display: 'Male' }
    ];

    When select, we want only the value F or M.

    <!-- app.component.html -->
    ...
    <div>
        <label>Gender</label>
        <div *ngFor="let gender of genders">
            <label>
            <input type="radio" name="gender" [(ngModel)]="user.gender" 
            [value]="gender.value">
            {{gender.display}}
            </label>
        </div>
    </div>
    ...

    4. Select (Primitive type)

    You can bind select to ngModel. Loop through your option list, set the value property. We have a list of roles:-

    // app.component.ts
    
    public roles = [
        { value: 'admin', display: 'Administrator' },
        { value: 'guest', display: 'Guest' },
        { value: 'custom', display: 'Custom' }
    ];

    When value selected, we expected it to return string value admin, guest or custom. Hereโ€™s how your HTML will look like.

    <!-- app.component.html -->
    ...
    <div>
        <label>Role</label>
        <select name="role" [(ngModel)]="user.role">
            <option *ngFor="let role of roles" [value]="role.value">  
            {{role.display}}
            </option>
        </select>
    </div>
    ...

    5. Select (object)

    Similar to last example, but this time, instead of simple type, we want the whole object when itโ€™s selected. Here is the list of themes:-

    // app.component.ts
    
    public themes: Theme[] = [
        { backgroundColor: 'black', fontColor: 'white', display: 'Dark' },
        { backgroundColor: 'white', fontColor: 'black', display: 'Light' },
        { backgroundColor: 'grey', fontColor: 'white', display: 'Sleek' }
    ];

    When selected, for example Light theme, we expect { backgroundColor: 'white', fontColor: 'black', display: 'Light' } to be returned. Instead of binding to the value property, we bind to ngValue property.

    <!-- app.component.html -->
    ...
    <div>
        <label>Theme</label>
        <select name="theme" [(ngModel)]="user.theme">
            <option *ngFor="let theme of themes" [ngValue]="theme">  
                {{theme.display}}
            </option>
        </select>
    </div>
    
    ...

    6. Multiple select

    We can select more than 1 topics. E.g. when selecting game and tech, it should return ['game', 'tech'].

    // app.component.ts
    
    public topics = [
        { value: 'game', display: 'Gaming' },
        { value: 'tech', display: 'Technology' },
        { value: 'life', display: 'Lifestyle' },
    ];

    Similar to select, but this time our model is array of string.

    <!-- app.component.html -->
    ...
    <div>
        <label>Topics</label>
        <select multiple name="topics" [(ngModel)]="user.topics">
            <option *ngFor="let topic of topics" [value]="topic.value">  
                {{topic.display}}
            </option>
        </select>
    </div>
    ...

    7. Checkbox (boolean)

    By default, checkbox return boolean. Bind ngModel and define name attribute as usual.

    <!-- app.component.html -->
    ...
    <div>
        <label>
            <input type="checkbox" name="isActive"  [(ngModel)]="user.isActive">
            Is Active
        </label>
    </div>
    
    ...

    8. Checkbox (Toggle value)

    In this case, we want to display a checkbox. Instead of boolean, we expecting value. When checked, it should return toggled, else return value untoggled.

    This is the list of toggles:-

    // app.component.ts
    
    public toggles = [
        { value: 'toggled', display: 'Toggled' },
        { value: 'untoggled', display: 'UnToggled' },
    ];

    First, we define a hidden input to bind to the real model. Then we create the checkbox input, handle the checked property and change event. Change event fire every time value change, and it has $event that we can read from.

    In our case, we read the $event.target.checked to find out if the checkbox is checked, then update model value accordingly.

    <!-- app.component.html -->
    ...
    <div>
        <input type="hidden" name="toggle" [(ngModel)]="user.toggle">
        <div>
            <label>
                <input type="checkbox"
                    [checked]="user.toggle === toggles[0].value" 
                    (change)="$event.target.checked? (user.toggle =  toggles[0].value) : (user.toggle = toggles[1].value)">
                {{ toggles[0].display }}
            </label>
        </div>
    </div>
    
    ...

    Tips during development

    During development, itโ€™s good that you can visualize the value. Angular provided a very useful json Pipe.

    <!-- app.component.html -->
    ...
    <pre>{{your_form or control_name | json }}</pre>
    
    ...

    Summary

    Thatโ€™s it. Hope it helps your journey in Angular 2. Happy coding!

    Jecelyn Yeen

    22 posts

    Coder. Diver. Board Game Lover.

    Speak English, Mandarin, JavaScript, Typescript, C# and more.

    GDE | Angular | Web Technologies.