Forms Guideline

This Forms Guideline document was created to help Angular developers build gorgeous and functional forms. The knowledge we are sharing here comes from years of experience with building forms, interacting with other developers building forms, and keeping up with industry best-practices.

Some concepts that will be covered include how to structure horizontal or vertical form layouts, how to logically separate your form components in to groups with separators, handling hint and validation messages, automatically displaying content based on the state of a form element, and much, much more.

These examples showcase both how to build Angular forms with Kendo UI for Angular components as well as native HTML elements so the guidance provided here can be used by anyone. To see any of the examples in action click on the "Open in StackBlitz" link at the bottom of each sample.

We hope that you find this guide useful! Now, let's kick things off by diving in to various form controls.

Form Controls

Forms consist of form controls, such as text fields, buttons, checkboxes, range controls, dropdowns, colorpickers, etc. Building forms with Kendo UI for Angular allows you to take full advantage of the Angular features and use Kendo components and/or native HTML controls.


import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <h5>BOOK YOUR DREAM VACATION TODAY</h5>
                <kendo-formfield>
                    <kendo-label [for]="fullName" text="Full Name"></kendo-label>
                    <input kendoTextBox #fullName [formControlName]="'fullName'" />
                    <kendo-formerror>Error: Full Name is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="email" text="Email"></kendo-label>
                    <input [formControlName]="'email'" kendoTextBox #email/>
                    <kendo-formerror *ngIf="form.controls.email.errors?.required">Error: Email is required</kendo-formerror>
                    <kendo-formerror *ngIf="form.controls.email.errors?.email">Error: Not valid email format</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="phoneNumber" text="Phone Number"></kendo-label>
                    <kendo-maskedtextbox #phoneNumber [formControlName]="'phoneNumber'" [mask]="phoneNumberMask" [value]="phoneNumberValue"></kendo-maskedtextbox>
                    <kendo-formerror>
                        <div *ngIf="form.controls.phoneNumber.errors">Error: Not a valid phone number format</div>
                        <div *ngIf="form.controls.phoneNumber.errors?.required">Error: Phone number is required</div>
                    </kendo-formerror>
                </kendo-formfield>

                <div class="wrap">
                    <kendo-formfield class="arrival-date">
                      <kendo-label [for]="arrivalDate" text="Arrival Date"></kendo-label>
                      <kendo-datepicker #arrivalDate [formControlName]="'arrivalDate'"></kendo-datepicker>
                        <kendo-formerror>Error: Arrival date is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <kendo-label [for]="numberOfNights" text="Number of Nights"></kendo-label>
                        <kendo-numerictextbox #numberOfNights [formControlName]="'numberOfNights'" [min]="0"></kendo-numerictextbox>
                            <kendo-formerror>Error: required</kendo-formerror>
                    </kendo-formfield>
                </div>

                <kendo-formfield>
                    <kendo-label [for]="numberOfGuests" text="Number of Guests"></kendo-label>
                    <kendo-numerictextbox #numberOfGuests [formControlName]="'numberOfGuests'" [min]="0" [max]="5"></kendo-numerictextbox>
                        <kendo-formhint [align]="'start'">
                            Maximum 5 guests. Babies (under 2 years old) are not included in the number of guests.
                        </kendo-formhint>
                        <kendo-formerror *ngIf="form.controls.numberOfGuests.errors?.required">Error: Number of guests is required</kendo-formerror>

                        <kendo-formerror *ngIf="form.controls.numberOfGuests.errors?.max">Error: Maximum 5 guests</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <div class="k-form-field-checkbox-wrap">
                        <input kendoCheckBox id="terms" type="checkbox" [formControlName]="'terms'" />
                        <kendo-label class="k-checkbox-label" for="terms" text="I agree with Тerms and Conditions"></kendo-label>
                    </div>
                    <kendo-formerror>Error: This field is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="comments" text="Comments" [optional]="true"></kendo-label>
                    <textarea kendoTextArea #comments [formControlName]="'comments'"></textarea>
                </kendo-formfield>

                 <div class="k-form-buttons">
                    <button class="k-button k-primary" (click)="submitForm()">Send Reservation Request</button>
                    <button class="k-button" (click)="clearForm()">Clear</button>
                </div>
            </form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: center;
        }
        .wrap {
          display: flex;
          justify-content: space-between;
        }
        .wrap .arrival-date {
          width: 90%;
          margin-right: 18px;
        }
        textarea { resize: vertical; }
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})

export class AppComponent {
    public phoneNumberValue: string = '';
    public phoneNumberMask: string = '(999) 000-00-00-00';

    public form: FormGroup;

    public data: any = {
        fullName: '',
        email: '',
        phoneNumber: this.phoneNumberValue,
        arrivalDate: null,
        numberOfNights: null,
        numberOfGuests: null,
        terms: false,
        comments: ''
    };

    constructor() {
        this.form = new FormGroup({
            fullName: new FormControl(this.data.fullName, [Validators.required]),
            email: new FormControl(this.data.email, [Validators.required, Validators.email]),
            phoneNumber: new FormControl(this.data.phoneNumber, [Validators.required]),
            arrivalDate: new FormControl(this.data.arrivalDate, [Validators.required]),
            numberOfNights: new FormControl(this.data.numberOfNights, [Validators.required]),
            numberOfGuests: new FormControl(this.data.numberOfGuests, [Validators.required, Validators.max(5)]),
            terms: new FormControl(this.data.terms, [Validators.requiredTrue]),
            comments: new FormControl(this.data.comments)
        });
    }

     public submitForm(): void {
        this.form.markAllAsTouched();
    }

     public clearForm(): void {
        this.form.reset();
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';

import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        DateInputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

FormField Component

The FormField enables you to group, align and control form-related content, such as hint and error messages associated with Kendo components or HTML input elements.

The component provides a way to configure the visibility of the hints - HintComponent and errors - ErrorComponent depending on the form-bound control state. See more at the API Reference of the FormField article.

Each kendo-formfield element can contain a single form control element, a label and multiple hint or error messages. The only exception to this rule is а RadioButton group.

The FormField component associates the control and its visible hint and error messages by assigning the aria-describedby attribute to the focusable element. As a result, screen readers will read out the messages when the user focuses the form control, making it easier for assistive technology users to understand what data should be entered.

Inputs

The following example demonstrates Kendo Inputs within a form in action:


import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="firstName" text="First Name"></kendo-label>
                    <input kendoTextBox #firstName [formControlName]="'firstName'" />
                    <kendo-formerror>Error: First Name is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showHints]="'initial'" [showErrors]="'initial'">
                    <kendo-label [for]="phoneNumber" text="Phone Number"></kendo-label>
                    <kendo-maskedtextbox #phoneNumber [formControlName]="'phoneNumber'" [mask]="phoneNumberMask" [value]="phoneNumberValue"></kendo-maskedtextbox>

                    <kendo-formhint [align]="'start'">Hint: Your active phone number</kendo-formhint>
                    <kendo-formerror>
                        <div *ngIf="form.controls.phoneNumber.errors">Error: Not a valid phone number format</div>
                        <div *ngIf="form.controls.phoneNumber.errors?.required">Error: Phone number is required</div>
                    </kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showHints]="'initial'" [showErrors]="'always'">
                    <kendo-label [for]="amount" text="Amount"></kendo-label>
                    <kendo-numerictextbox #amount [formControlName]="'amount'" [min]="0"></kendo-numerictextbox>
                        <kendo-formhint [align]="'start'">
                            Hint: Amount
                        </kendo-formhint>
                        <kendo-formerror *ngIf="(form.controls.amount.touched || form.controls.amount.dirty) && form.controls.amount.errors?.required">Error: Amount is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="color" text="Choose Color"></kendo-label>
                    <kendo-colorpicker #color [formControlName]="'color'" [view]="'palette'" [paletteSettings]="{ palette: 'austin' }"></kendo-colorpicker>

                    <kendo-formerror>
                        Error: Color is required
                    </kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="price" text="Price Limit"></kendo-label>
                    <kendo-slider [formControlName]="'price'" [min]="0" [max]="600" [smallStep]="50"></kendo-slider>
                    <kendo-formhint>Choose a price limit</kendo-formhint>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="allow" [optional]="true" text="Allow Notifications"></kendo-label>
                    <kendo-switch #allow [formControlName]="'notifications'"></kendo-switch>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="comments" text="Additional Notes"></kendo-label>
                    <textarea kendoTextArea #comments [formControlName]="'comments'"></textarea>
                </kendo-formfield>
            </form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: center;
        }
        textarea { resize: vertical; }
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})

export class AppComponent {
    public phoneNumberValue: string = '';
    public phoneNumberMask: string = '(999) 000-00-00-00';

    public form: FormGroup;

    public userData: any = {
        firstName: '',
        phoneNumber: this.phoneNumberValue,
        amount: 0,
        color: null,
        price: null,
        notifications: true,
        comments: ''
    };

    constructor() {
        this.form = new FormGroup({
            firstName: new FormControl(this.userData.firstName, [Validators.required]),
            phoneNumber: new FormControl(this.userData.phoneNumber, [Validators.required]),
            amount: new FormControl(this.userData.amount, [Validators.required]),
            color: new FormControl(this.userData.color, [Validators.required]),
            price: new FormControl(this.userData.price),
            notifications: new FormControl(this.userData.notifications),
            comments: new FormControl(this.userData.comments)
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Checkboxes & RadioButtons

The following example demonstrates Kendo Checkboxes and RadioButtons within a form in action:

import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <kendo-formfield [showHints]="'initial'">
                    <kendo-label class="k-label" text="Type of Confirmation"></kendo-label>
                        <ul class="k-radio-list">
                            <li class="k-radio-item">
                                <input type="radio" #phoneCall value="phoneCall" kendoRadioButton [formControlName]="'confirmation'" />
                                <kendo-label class="k-radio-label" [for]="phoneCall" text="Phone Call"></kendo-label>
                            </li>

                            <li class="k-radio-item">
                                <input type="radio" #email kendoRadioButton  value="email" [formControlName]="'confirmation'" />
                                <kendo-label class="k-radio-label" [for]="email" text="Via Email"></kendo-label>
                            </li>
                        </ul>
                        <kendo-formhint>Hint: Choose a way to receive a confirmation</kendo-formhint>
                        <kendo-formerror>Error: This field is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [orientation]="'horizontal'" [showHints]="'initial'">
                    <label class="k-label">Gender</label>

                    <ul class="k-radio-list k-list-horizontal">
                        <li class="k-radio-item">
                            <input type="radio" #male value="male" kendoRadioButton [formControlName]="'gender'" />
                            <kendo-label class="k-radio-label" [for]="male" text="Male"></kendo-label>
                        </li>

                        <li class="k-radio-item">
                            <input type="radio" #female value="female" kendoRadioButton [formControlName]="'gender'" />
                            <kendo-label class="k-radio-label" [for]="female" text="Female"></kendo-label>
                        </li>

                        <li class="k-radio-item">
                            <input type="radio" #other value="other" kendoRadioButton [formControlName]="'gender'" />
                            <kendo-label class="k-radio-label" [for]="other" text="Other"></kendo-label>
                        </li>
                    </ul>

                    <kendo-formerror>Error: This field is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showHints]="'initial'">
                    <div class="k-form-field-checkbox-wrap">
                        <input kendoCheckBox id="terms" type="checkbox" [formControlName]="'terms'" />
                        <kendo-label class="k-checkbox-label" for="terms" text="I agree with terms and conditions"></kendo-label>
                    </div>

                    <kendo-formhint>By checking this, you agree to our Terms&Conditions</kendo-formhint>
                    <kendo-formerror>Error: This field is required</kendo-formerror>
                </kendo-formfield>

                <div class="k-form-buttons">
                    <button class="k-button k-primary" (click)="submitForm()">Submit</button>
                </div>
            </form>
        </div>
    `,
    styles: [`
        .k-form {
            width: 350px;
            padding: 20px;
        }
        .k-list-horizontal .k-radio-item {
            margin: 0 10px 0 0;
        }
    `]
})
export class AppComponent {
    public form: FormGroup;

    public data: any = {
        terms: '',
        confirmation: '',
        gender: null,
        amount: null
    };

    constructor() {
        this.form = new FormGroup({
            terms: new FormControl(this.data.terms, [Validators.requiredTrue]),
            confirmation: new FormControl(this.data.confirmation, [Validators.required]),
            gender: new FormControl(this.data.gender, [Validators.required]),
            amount: new FormControl(this.data.amount, [Validators.required]),
        });
    }

    public submitForm(): void {
        this.form.controls.confirmation.markAsTouched();
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Uploads

The following example demonstrates Kendo Upload and FileSelect within a form in action:

import { Component, Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpProgressEvent, HttpEventType, HttpResponse } from '@angular/common/http';
import { Observable, of, concat } from 'rxjs';
import { delay } from 'rxjs/operators';

@Component({
    selector: 'my-app',
    template: `<form-uploads></form-uploads>`
})
export class AppComponent {}

/*
  Mocked backend service.
  For further details, check
  https://angular.io/guide/http#writing-an-interceptor
*/

@Injectable()
export class UploadInterceptor implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (req.url === 'saveUrl') {
            const events: Observable<HttpEvent<any>>[] = [0, 30, 60, 100].map((x) => of(<HttpProgressEvent>{
                type: HttpEventType.UploadProgress,
                loaded: x,
                total: 100
            }).pipe(delay(1000)));

            const success = of(new HttpResponse({ status: 200 })).pipe(delay(1000));
            events.push(success);

            return concat(...events);
        }

        if (req.url === 'removeUrl') {
            return of(new HttpResponse({ status: 200 }));
        }

        return next.handle(req);
      }
}
import { Component, OnInit } from '@angular/core';
import { FileRestrictions } from '@progress/kendo-angular-upload';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';

@Component({
  selector: 'form-uploads',
  template: `
    <div class="example">
        <form class="k-form" [formGroup]="form" novalidate (ngSubmit)="save(form.value, form.valid)">
            <kendo-formfield>
                <kendo-label [for]="avatar" [text]="'Avatar'"></kendo-label>
                <kendo-upload
                    #avatar
                    [formControlName]="'avatar'"
                    [saveUrl]="uploadSaveUrl"
                    [removeUrl]="uploadRemoveUrl"
                    [restrictions]="restrictions">
                </kendo-upload>

                <kendo-formhint>Hint: Upload your avatar picture</kendo-formhint>
                <kendo-formerror *ngIf="form.controls.avatar.errors?.required && submitted">Error: Uploading an avatar is required</kendo-formerror>
            </kendo-formfield>

            <kendo-formfield>
                <kendo-label [for]="files" [text]="'Upload files'"></kendo-label>
                <kendo-fileselect #files [formControlName]="'files'" [multiple]="true"></kendo-fileselect>

                <kendo-formhint>Hint: Select your files</kendo-formhint>
                <kendo-formerror *ngIf="form.controls.files.errors?.required && submitted">Error: Uploading files is required</kendo-formerror>
            </kendo-formfield>

            <button type="submit" class="k-button k-primary" style="margin: 1em 0;">Submit</button>
        </form>
    </div>
  `
})
export class UploadsComponent implements OnInit {
    uploadSaveUrl = 'saveUrl'; // should represent an actual API endpoint
    uploadRemoveUrl = 'removeUrl'; // should represent an actual API endpoint

    public data: any = {
        avtar: [],
        files: []
    }
    public form: FormGroup;

    public submitted = false;
    public restrictions: FileRestrictions = {
        allowedExtensions: ['jpg', 'jpeg', 'png']
    };

    public ngOnInit(): void {
        this.form = new FormGroup({
            avatar: new FormControl(this.data.avtar, [Validators.required]),
            files: new FormControl(this.data.files, [Validators.required])
        });
    }

    public save(value: any, valid: boolean): void {
        this.submitted = true;

        if (valid) {
            console.log('Everything is OK!');
        } else {
            this.form.markAllAsTouched();
        }
    }
}

import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';

import { UploadsModule } from '@progress/kendo-angular-upload';
import { UploadsComponent } from './uploads.component';
import { UploadInterceptor } from './app.component';
import { AppComponent } from './app.component';

@NgModule({
    imports: [
        BrowserModule,
        HttpClientModule,
        UploadsModule,
        BrowserAnimationsModule,
        FormsModule,
        ReactiveFormsModule,
        LabelModule,
        InputsModule
    ],
    declarations: [ AppComponent, UploadsComponent ],
    bootstrap:    [ AppComponent ],
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: UploadInterceptor,
            multi: true
        }
    ]
})

  export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

DropDowns

The following example demonstrates Kendo DropDowns within a form in action:

import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

import { countries, genders, sizes, sports } from './data';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
        <form class="k-form" [formGroup]="form">
                <kendo-formfield [showHints]="'initial'" [showErrors]="'initial'">
                    <kendo-label [for]="country" text="Country"></kendo-label>
                    <kendo-autocomplete
                        #country
                        [data]="countries"
                        [formControlName]="'country'"
                    >
                    </kendo-autocomplete>

                    <kendo-formhint [align]="'start'">Hint: Only Eroupean countries</kendo-formhint>
                    <kendo-formerror *ngIf="!(form.controls.country.required)">Error: Country is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="gender" text="Gender"></kendo-label>
                    <kendo-combobox
                        #gender
                        [formControlName]="'gender'"
                        [data]="genders"
                        [textField]="'text'"
                        [valueField]="'value'"
                        [valuePrimitive]="true"
                    >
                    </kendo-combobox>
                    <kendo-formerror *ngIf="!(form.controls.gender.required)">Error: Gender is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="size" text="T-Shirt Size"></kendo-label>
                    <kendo-dropdownlist
                        #size
                        [formControlName]="'size'"
                        [data]="sizes"
                    >
                    </kendo-dropdownlist>
                    <kendo-formerror *ngIf="!(form.controls.size.required)">Error: Size is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="sport" text="Sport"></kendo-label>
                    <kendo-multiselect
                        #sport
                        [formControlName]="'sport'"
                        [data]="sports"
                    >
                    </kendo-multiselect>
                    <kendo-formhint [align]="'start'">Hint: Your favourite sport</kendo-formhint>
                    <kendo-formerror *ngIf="!(form.controls.sport.required)">Error: Sport is required</kendo-formerror>
                </kendo-formfield>
            </form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: center;
        }
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})

export class AppComponent {
    public form: FormGroup;

    public userData: any = {
        country: '',
        gender: '',
        size: '',
        sport: ''
    };

    public countries: Array<string> = countries;
    public sports: Array<string> = sports;
    public sizes: Array<string> = sizes;
    public genders: Array<{ text: string, value: number }> = genders;

    constructor() {
        this.form = new FormGroup({
            country: new FormControl(this.userData.country, [Validators.required]),
            gender: new FormControl(this.userData.gender, [Validators.required]),
            size: new FormControl(this.userData.size, [Validators.required]),
            sport: new FormControl(this.userData.sport, [Validators.required])
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
import { InputsModule } from '@progress/kendo-angular-inputs';

import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        DropDownsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
export const genders: Array<{ text: string, value: number }> = [
    { text: 'Male', value: 1 },
    { text: 'Female', value: 2 },
    { text: 'Other', value: 3 }
];

export const countries: Array<string> = [
    'Albania',
    'Andorra',
    'Armenia',
    'Austria',
    'Azerbaijan',
    'Belarus',
    'Belgium',
    'Bosnia & Herzegovina',
    'Bulgaria',
    'Croatia',
    'Cyprus',
    'Czech Republic',
    'Denmark',
    'Estonia',
    'Finland',
    'France',
    'Georgia',
    'Germany',
    'Greece',
    'Hungary',
    'Iceland',
    'Ireland',
    'Italy',
    'Kosovo',
    'Latvia',
    'Liechtenstein',
    'Lithuania',
    'Luxembourg',
    'Macedonia',
    'Malta',
    'Moldova',
    'Monaco',
    'Montenegro',
    'Netherlands',
    'Norway',
    'Poland',
    'Portugal',
    'Romania',
    'Russia',
    'San Marino',
    'Serbia',
    'Slovakia',
    'Slovenia',
    'Spain',
    'Sweden',
    'Switzerland',
    'Turkey',
    'Ukraine',
    'United Kingdom',
    'Vatican City'
];

export const sizes: Array<string> = ['X-Small', 'Small', 'Medium', 'Large', 'X-Large', '2X-Large'];

export const sports: Array<string> = ['Baseball', 'Basketball', 'Cricket', 'Field Hockey', 'Football', 'Table Tennis', 'Tennis', 'Volleyball'];
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

DateInputs

The following example demonstrates Kendo DateInputs within a form in action:

import { OnInit, Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
        <form class="k-form" [formGroup]="form">
                <kendo-formfield [showHints]="'initial'" [showErrors]="'initial'">
                    <kendo-label [for]="appointment" text="Appointment Date and Time"></kendo-label>
                        <kendo-datetimepicker #appointment [formControlName]="'appointment'">
                        </kendo-datetimepicker>

                    <kendo-formhint [align]="'start'">Hint: Select Date and Time for your appointment</kendo-formhint>
                    <kendo-formerror *ngIf="form.controls.appointment.errors?.required">Error: Date and Time are required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="birthDate" text="Birth Date"></kendo-label>
                    <kendo-dateinput #birthDate [formControlName]="'birthDate'"></kendo-dateinput>

                    <kendo-formerror *ngIf="form.controls.birthDate.errors?.required">Error: Birth Date is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showHints]="'initial'" [showErrors]="'initial'">
                    <kendo-label [for]="birthTime" text="Time of Birth"></kendo-label>
                    <kendo-timepicker #birthTime [formControlName]="'birthTime'"></kendo-timepicker>

                    <kendo-formhint [align]="'start'">Hint: Select your time of birth</kendo-formhint>
                    <kendo-formerror *ngIf="form.controls.birthTime.errors?.required">Error: Time of birth is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="date" text="Pick a Date"></kendo-label>
                    <kendo-datepicker #date [formControlName]="'date'"></kendo-datepicker>
                    <kendo-formerror *ngIf="form.controls.date.errors?.required">Error: Date is required</kendo-formerror>
                </kendo-formfield>

                <kendo-daterange>
                    <kendo-formfield [showErrors]="'initial'">
                        <kendo-label [for]="subscriptionStart" text="Subscription Start Date"></kendo-label>
                        <kendo-dateinput
                            #subscriptionStart
                            kendoDateRangeStartInput
                            [formControlName]="'subscriptionStart'"
                        >
                        </kendo-dateinput>
                        <kendo-formerror *ngIf="form.controls.subscriptionStart.errors?.required">Error: Subscription Start Date is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield [showErrors]="'initial'">
                        <kendo-label [for]="subscriptionEnd" text="Subscription End Date"></kendo-label>
                        <kendo-dateinput
                            #subscriptionEnd
                            kendoDateRangeEndInput
                            [formControlName]="'subscriptionEnd'"
                        >
                        </kendo-dateinput>
                        <kendo-formerror *ngIf="form.controls.subscriptionEnd.errors?.required">Error: Subscription End Date is required</kendo-formerror>
                    </kendo-formfield>
                </kendo-daterange>
            </form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: center;
        }
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})

export class AppComponent {
    public form: FormGroup;

    public userData: any = {
        appointment: null,
        birthDate: null,
        birthTime: null,
        subscriptionStart: null,
        subscriptionEnd: null,
        date: null
    };

    constructor() {
        this.form = new FormGroup({
            appointment: new FormControl(this.userData.appointment, [Validators.required]),
            birthDate: new FormControl(this.userData.birthDate, [Validators.required]),
            birthTime: new FormControl(this.userData.birthTime, [Validators.required]),
            subscriptionStart: new FormControl(this.userData.subscriptionStart, [Validators.required]),
            subscriptionEnd: new FormControl(this.userData.subscriptionEnd, [Validators.required]),
            date: new FormControl(this.userData.date, [Validators.required])
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
import { InputsModule } from '@progress/kendo-angular-inputs';

import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        DateInputsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Readonly & Disabled Controls

Rendering the control in a readonly state makes it immutable and the user cannot edit it. The readonly controls are still responsive to interaction, can be focused and participate in constraint validation.

Rendering the control in a disabled state makes it immutable. By default, in both Reactive and Template-driven Angular Forms, the disabled form field values are excluded from the FormGroup value object. The disabled controls are not responsive to user interaction and do not participate in constraint validation.

Defining a disabled control in Reactive Angular forms can be accomplished by adding Validator functions directly to the form control model in the component class.

import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <div>Submitted data: {{ form.value | json }}</div>
            <form class="k-form" [formGroup]="form">
                <kendo-formfield>
                    <kendo-label [for]="ownerName" text="Owner Name"></kendo-label>
                    <input kendoTextBox #ownerName [formControlName]="'ownerName'" />
                    <kendo-formhint>Hint: Disabled field</kendo-formhint>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="phoneNumber" text="Phone Number"></kendo-label>
                    <kendo-maskedtextbox
                        #phoneNumber
                        [formControlName]="'phoneNumber'"
                        [mask]="phoneNumberMask"
                        [value]="phoneNumberValue"
                        readonly="true">
                    </kendo-maskedtextbox>

                    <kendo-formhint [align]="'start'">Hint: Readonly field</kendo-formhint>
                </kendo-formfield>

                <div class="k-form-buttons">
                    <button class="k-button k-primary" (click)="submit()">Submit</button>
                </div>
            </form>
        </div>
    `,
    styles: [`
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})

export class AppComponent {
    public phoneNumberValue: string = '359884123321';
    public phoneNumberMask: string = '(999) 000-00-00-00';

    public form: FormGroup;

    public data: any = {
        ownerName: 'Chuck Norris',
        phoneNumber: this.phoneNumberValue
    };

    constructor() {
        this.form = new FormGroup({
            ownerName: new FormControl({ value: this.data.ownerName, disabled: true }, [Validators.required]),
            phoneNumber: new FormControl(this.data.phoneNumber, [Validators.required])
        });
    }

    public submit(): void {
        console.log(this.form.value);
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Sizing

By default, all form controls nested in a form with .k-form class will take 100% of their parent element’s width, except for the ColorPicker, Switch and Slider components.

Labeling

Labels are not just visually associated with form control elements, but programmatically as well. Assistive technologies will read the label content when the form element is focused. Additionally, on label click, the form element will receive focus, providing an increased hit area to activate it.

Labels

The Label component associates a label tag with Angular component by template reference.When the form control is a plain HTML element, a simple ID is used to associate the pair. Additionally, it provides the option to mark it as an “Optional” form field.

Floating Labels

Тhe Floating label is an inline label which floats above the input when the user has focused the form field or has entered a value. One of its benefits is that it looks good and the resolved animation when moving to the next field is giving users a sense of completion.

The FloatingLabel component supports both Template-driven and Reactive Forms and can contain a Kendo UI for Angular Input or an HTML input element.

import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <kendo-formfield>
                    <kendo-floatinglabel [text]="'Age'">
                        <kendo-numerictextbox [formControlName]="'age'"></kendo-numerictextbox>
                    </kendo-floatinglabel>
                    <kendo-formhint>Hint: Enter your age</kendo-formhint>
                    <kendo-formerror>Error: Age is required</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-floatinglabel [text]="'Street Number'">
                        <kendo-numerictextbox [formControlName]="'streetNumber'"></kendo-numerictextbox>
                    </kendo-floatinglabel>
                    <kendo-formhint>Hint: Enter your street number</kendo-formhint>
                    <kendo-formerror>Error: Street Number is required</kendo-formerror>
                </kendo-formfield>
            </form>
        </div>
    `,
    encapsulation: ViewEncapsulation.None,
    styles: [`
        .k-form { width: 400px; }
    `]
})
export class AppComponent {
    public form: FormGroup;

    public data: any = {
        age: null,
        streetNumber: null
    };

    constructor() {
        this.form = new FormGroup({
            age: new FormControl(this.data.age, [Validators.required]),
            streetNumber: new FormControl(this.data.streetNumber, [Validators.required]),
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Optional Labels

The way we visualize a form field has a strong implication for how users perceive and complete forms. As an example, instead of marking every field as required you can only mark the optional fields. This results in less visual noise, since there will be less red marks across our user interface, and this will lead to users completing the form faster.

Both the Label and FloatingLabel components have an optional Boolean property, which if set to true will render “(Optional)” text inside the label element of the form control. The text is customizable via custom messages or the Angular i18n framework. Refer to the article on Globalization for further details and runnable demos.

import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <kendo-formfield>
                    <kendo-floatinglabel [optional]="true" [text]="'Street Number'">
                        <kendo-numerictextbox [formControlName]="'streetNumber'"></kendo-numerictextbox>
                    </kendo-floatinglabel>
                    <kendo-formhint>Hint: Enter your street number</kendo-formhint>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="phoneNumber" [optional]="true" [text]="'Phone Number'"></kendo-label>
                    <kendo-maskedtextbox #phoneNumber [formControlName]="'phoneNumber'" [mask]="phoneNumberMask"></kendo-maskedtextbox>

                    <kendo-formhint [align]="'start'">Hint: Your active phone number</kendo-formhint>
                </kendo-formfield>
            </form>
        </div>
    `,
    encapsulation: ViewEncapsulation.None,
    styles: [`
        .k-form { width: 400px; }
    `]
})
export class AppComponent {
    public phoneNumberMask: string = '(999) 000-00-00-00';

    public form: FormGroup;

    public data: any = {
        streetNumber: null,
        phoneNumber: ''
    };

    constructor() {
        this.form = new FormGroup({
            streetNumber: new FormControl(this.data.streetNumber),
            phoneNumber: new FormControl(this.data.phoneNumber)
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Placeholder Texts

The placeholder text typically has a lighter color treatment to indicate that it is a suggestion for what kind of input will be valid.

Hint Messages

Hint messages provide additional context about the expected value of a form control. For example, in a registration form where users have to provide a password containing at least 3 characters, a hint message aligned under the input could avoid any confusion.

import { Component, ViewEncapsulation } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <kendo-formfield>
                    <kendo-label [for]="userName" [text]="'Username'"></kendo-label>
                    <input [formControlName]="'userName'" kendoTextBox #userName/>

                    <kendo-formhint [align]="'start'">Hint: Hint message aligned start</kendo-formhint>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="date" [text]="'Birth Date'"></kendo-label>
                    <kendo-datepicker #date [formControlName]="'date'"></kendo-datepicker>

                    <kendo-formhint [align]="'end'">Hint: Hint message aligned end</kendo-formhint>
                </kendo-formfield>
            </form>
        </div>
    `,
    styles: [`
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {
    public form: FormGroup;

    constructor() {
        this.form = new FormGroup({
            userName: new FormControl(),
            date: new FormControl()
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule,
        DateInputsModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Validation

Form validation is used to make sure the user has provided correct information in terms of format, content length, etc. For example, is the Email in actual email format, did the user put info in all of the required fields, and so on.

How it Works?

Kendo UI for Angular components support handling user input in the UI and display useful validation messages using both Reactive and Template-driven Angular forms. The validation rules refer to specific checks, managed client-side. Server-side validation is not handled by the components, but is still an essential part of the application security, and should be addressed by the developer.

Field-level Validation

Field-level validation ensures that the value entered in the field is in accordance with the application requirements. If the validation rules are not satisfied, error messages for each field are displayed.

To keep the form UI clean, the error messages of an invalid control are usually not shown until specific user interaction. There are several common factors when to display a validation message:

  • The control was edited—dirty
  • The control was blurred—touched
  • Always—Show error messages regardless of user interaction

The FormField component enables you to control when and how validation messages will be shown in compliance with the Angular form validation guide.

Error Messages

For instant validation, error messages are the best way for alerting users that they have made a mistake while filling out a form. Applying only error-specific styles does not convey enough information about what the user should do to provide valid data. Error messages should specify exactly why the user input is not accepted and should be shown one field at a time.

import { Component, ViewEncapsulation } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="userForm">
                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="userName" text="Username"></kendo-label>
                    <input formControlName="userName" kendoTextBox #userName required/>

                    <kendo-formerror *ngIf="userForm.controls.userName.errors?.required">Error: Username is required</kendo-formerror>
                    <kendo-formerror *ngIf="userForm.controls.userName.errors?.minlength">Error: Username has to be at least 3 characters</kendo-formerror>
                    <kendo-formerror *ngIf="userForm.controls.userName.errors?.maxlength">Error: Username has to be less than 12 characters</kendo-formerror>
                </kendo-formfield>

                <kendo-formfield [showErrors]="'initial'">
                    <kendo-label [for]="email" text="Email"></kendo-label>
                    <input formControlName="email" kendoTextBox #email required/>

                    <kendo-formerror *ngIf="userForm.controls.email.errors?.required">Error: Email is required</kendo-formerror>
                    <kendo-formerror *ngIf="userForm.controls.email.errors?.email">Error: Not valid email format</kendo-formerror>
                </kendo-formfield>
            </form>
        </div>
    `,
    styles: [`
        .k-form { width: 400px; }
    `],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {

    public userForm: FormGroup = new FormGroup({
        userName: new FormControl('', [Validators.minLength(3), Validators.maxLength(12)]),
        email: new FormControl('', Validators.email)
    });
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Form-level Validation

Form-level validation is the process of validating all fields in a form at once. It is usually called when the user is ready to proceed to another step or clicks the Submit button.

Validation Summary

The validation summary displays a list of all the validation errors
in a form in one location. This approach can be useful when dealing with large forms.

import { Component, ViewEncapsulation } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example-wrap">
            <validation-summary [form]="form" *ngIf="submitted"></validation-summary>

            <form class="k-form" [formGroup]="form">
                <kendo-formfield>
                    <kendo-label [for]="userName" text="Username"></kendo-label>
                    <input formControlName="userName" kendoTextBox #userName required/>
                </kendo-formfield>

                <kendo-formfield>
                    <kendo-label [for]="email" text="Email"></kendo-label>
                    <input formControlName="email" kendoTextBox #email required/>
                </kendo-formfield>

                <kendo-formfield>
                    <div class="k-checkbox-wrap">
                        <input #acceptNews type="checkbox" kendoCheckBox formControlName="acceptNews"/>
                        <kendo-label [for]="acceptNews" class="k-checkbox-label" text="I want to receive notifications"></kendo-label>
                    </div>
                    <kendo-formhint>You will recieve our latest updates and promotions on your email</kendo-formhint>
                </kendo-formfield>

                <div class="k-form-buttons">
                    <button class="k-button k-primary" (click)="submitForm()">Submit</button>
                </div>
            </form>
        </div>
    `,
    styles: [`
        .k-form, .k-messagebox {
            width: 400px;
        }
        .k-messagebox ul {
            padding-left: 20px;
        }
    `],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {
    public submitted: boolean = false;

    public form: FormGroup = new FormGroup({
        userName: new FormControl('', [Validators.required, Validators.minLength(8)]),
        email: new FormControl('', Validators.email),
        acceptNews: new FormControl()
    });

    public submitForm(): void {
        this.submitted = true;
        this.form.markAllAsTouched();
    }
}
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';

@Component({
    selector: 'validation-summary',
    template: `
        <div class="k-messagebox k-messagebox-error" *ngIf="errors?.length > 0">
            <ul>
                <li *ngFor="let error of errors">{{ error }}</li>
            </ul>
        </div>
    `
})
export class ValidationSummaryComponent implements OnInit {
    @Input() public form: FormGroup;
    public errors: string[] = [];

    public ngOnInit(): void {
        this.generateErrorMessages();

        this.form.statusChanges.subscribe(() => {
            this.resetErrorMessages();
            this.generateErrorMessages();
        });
    }

    public resetErrorMessages(): void {
      this.errors.length = 0;
    }

    public generateErrorMessages(): void {
        const controlNames = Object.keys(this.form.controls);

        controlNames.forEach(controlName => {
            let control = this.form.controls[controlName];
            let errors = control.errors;

            if (errors === null || errors.length === 0) {
            return;
            }

            if (errors.required) {
            this.errors.push(`${controlName} is required`);
            }

            if (errors.minlength) {
            this.errors.push(`${controlName} minimum length is ${errors.minlength.requiredLength}.`);
            }

            if (errors.email){
            this.errors.push(`${controlName} is not a valid email`);
            }
        });
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { InputsModule } from '@progress/kendo-angular-inputs';
import { LabelModule } from '@progress/kendo-angular-label';

import { AppComponent } from './app.component';
import { ValidationSummaryComponent } from './validation-summary.component';

@NgModule({
    bootstrap: [AppComponent],
    declarations: [
        AppComponent, ValidationSummaryComponent
    ],
    imports: [
        CommonModule,
        BrowserModule,
        BrowserAnimationsModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Form Layout

When it comes to filling out a form, users want to move through it as quickly as possible without having to overthink anything. A great form layout leads to seamless completion and improves the speed of the submission process. Using a single-column layout has been shown to result in significantly better user understanding, fewer errors and higher overall rates of conversion.

Horizontal & Vertical

By default, most Kendo components are 100% wide when placed inside a kendo-formfield. As a result, kendo forms are stacked vertically.

To make a horizontal form layout and position form controls along with their labels on the same row, assign the k-form-horizontal class to the form element and set the orientation property of the FormField to horizontal.

import { Component, ViewEncapsulation } from '@angular/core';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <vertical-form></vertical-form>
            <horizontal-form></horizontal-form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: space-evenly;
        }
        .k-form {
            width: 420px;
        }

    `],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {}

import { Component, ViewEncapsulation } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
    selector: 'horizontal-form',
    template: `
        <div class="example">
            <form class="k-form k-form-horizontal" [formGroup]="form">
                <fieldset class="k-form-fieldset">
                    <legend class="k-form-legend">Horizontal Form</legend>
                    <kendo-formfield [orientation]="'horizontal'">
                        <kendo-label [for]="holder" text="Full Name"></kendo-label>
                        <input [formControlName]="'holder'" kendoTextBox #holder required />
                            <kendo-formhint>Hint: Card Holder full name</kendo-formhint>
                            <kendo-formerror>Error: Full Name is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield [orientation]="'horizontal'">
                        <kendo-label [for]="cardNumber" text="Card Number"></kendo-label>
                        <kendo-maskedtextbox
                            #cardNumber
                            [formControlName]="'cardNumber'"
                            [mask]="mask">
                        </kendo-maskedtextbox>

                        <kendo-formhint>Hint: Your Card Number</kendo-formhint>
                        <kendo-formerror>Error: Card Number is required format</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield [orientation]="'horizontal'">
                       <kendo-label [for]="expiration" text="Expiration Date"></kendo-label>
                       <kendo-dateinput #expiration [formControlName]="'expirationDate'"></kendo-dateinput>

                       <kendo-formerror>Error: Expiration Date is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield [orientation]="'horizontal'">
                        <kendo-label [for]="cvc" text="CVC Number"></kendo-label>
                        <kendo-maskedtextbox
                            #cvc
                            [formControlName]="'cvc'"
                            [mask]="cvcMask">
                        </kendo-maskedtextbox>

                        <kendo-formhint>Hint: The last 3 digids on the back of the Card</kendo-formhint>
                        <kendo-formerror>Error: Card CVC Number is required</kendo-formerror>
                        <kendo-formerror *ngIf="form.controls.cvc.errors?.maxLength">Error: Card CVC Number has to be exactly 3 number</kendo-formerror>
                        <kendo-formerror *ngIf="form.controls.cvc.errors?.minLength">Error: Card CVC Number has to be exactly 3 number</kendo-formerror>
                    </kendo-formfield>

                    <div class="k-form-buttons">
                        <button class="k-button k-primary" (click)="submitForm()">Submit</button>
                        <button class="k-button" (click)="clearForm()">Clear</button>
                    </div>
                </fieldset>
            </form>
        </div>
    `
})
export class HorizontalFormComponent {
    public mask: string = "0000-0000-0000-0000";
    public cvcMask: string = "000";

    public form: FormGroup = new FormGroup({
        holder: new FormControl('', Validators.required),
        cardNumber: new FormControl('', Validators.required),
        expirationDate: new FormControl('', Validators.required),
        cvc: new FormControl('', [Validators.required, Validators.maxLength(3), Validators.minLength(3)])
    });

    public submitForm(): void {
        this.form.markAllAsTouched();
    }

    public clearForm(): void {
        this.form.reset();
    }
}
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
    selector: 'vertical-form',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <fieldset class="k-form-fieldset">
                    <legend class="k-form-legend">Vertical Form</legend>
                    <kendo-formfield>
                        <kendo-label [for]="userName" text="Username"></kendo-label>
                        <input [formControlName]="'userName'" kendoTextBox #userName />

                        <kendo-formhint>Your username</kendo-formhint>
                        <kendo-formerror>Error: UserName is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <kendo-label [for]="email" text="Email" [optional]="true"></kendo-label>
                        <input [formControlName]="'email'" kendoTextBox #email />

                        <kendo-formhint>Your active email</kendo-formhint>
                        <kendo-formerror>Error: Not valid email format</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <div class="k-checkbox-wrap">
                            <input #acceptNews type="checkbox" kendoCheckBox [formControlName]="'acceptNews'"/>
                            <kendo-label [for]="acceptNews" class="k-checkbox-label" [text]="'I want to receive notifications'" [optional]="true"></kendo-label>
                        </div>
                        <kendo-formhint>You will receive our latest updates and promotions on your email</kendo-formhint>
                    </kendo-formfield>

                    <div class="k-form-buttons">
                        <button class="k-button k-primary" (click)="submitForm()">Submit</button>
                        <button class="k-button" (click)="clearForm()">Clear</button>
                    </div>
                </fieldset>
            </form>
        </div>
    `
})
export class VerticalFormComponent {
    public form: FormGroup = new FormGroup({
        userName: new FormControl('', Validators.required),
        lastName: new FormControl('', Validators.required),
        email: new FormControl('', Validators.email),
        acceptNews: new FormControl()
    });

    public submitForm(): void {
        this.form.markAllAsTouched();
    }

    public clearForm(): void {
        this.form.reset();
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';

import { VerticalFormComponent } from './vertical-form.component';
import { HorizontalFormComponent } from './horizontal-form.component';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent, VerticalFormComponent, HorizontalFormComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        DateInputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Groups & Separators

Grouping form controls in a meaningful way makes for a cleaner and more concise form content. Using the legend and fieldset structures allows showing relation between form controls, which is especially useful when it comes to large complex forms.

Separators add a line break to the layout improving the form conversion rate. The separation of sections allows you to make compound forms more approachable. To add a separator, add the k-form-separator class on a span element.

import { Component, ViewEncapsulation } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <fieldset class="k-form-fieldset">
                    <legend class="k-form-legend">User Details</legend>
                    <kendo-formfield>
                        <kendo-label [for]="firstName" text="First Name"></kendo-label>
                        <input [formControlName]="'firstName'" kendoTextBox #firstName required/>

                        <kendo-formhint>Your first name</kendo-formhint>
                        <kendo-formerror>Error: First name is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <kendo-label [for]="lastName" text="Last Name"></kendo-label>
                        <input [formControlName]="'lastName'" kendoTextBox #lastName required/>

                        <kendo-formhint>Your last name</kendo-formhint>
                        <kendo-formerror>Error: Last name is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <kendo-label [for]="email" text="Email"></kendo-label>
                        <input [formControlName]="'email'" kendoTextBox #email required/>

                        <kendo-formhint>Your active email</kendo-formhint>
                        <kendo-formerror>Error: Not valid email format</kendo-formerror>
                    </kendo-formfield>

                    <span class="k-form-separator"></span>

                    <kendo-formfield>
                        <div class="k-checkbox-wrap">
                            <input #acceptNews type="checkbox" kendoCheckBox [formControlName]="'acceptNews'"/>
                            <kendo-label [for]="acceptNews" class="k-checkbox-label" [text]="'I want to receive notifications'" [optional]="true"></kendo-label>
                        </div>

                        <kendo-formhint>You will receive our latest updates and promotions on your email</kendo-formhint>
                    </kendo-formfield>

                    <div class="k-form-buttons">
                        <button class="k-button k-primary" (click)="submitForm()">Submit</button>
                        <button class="k-button" (click)="clearForm()">Clear</button>
                    </div>
                </fieldset>
            </form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: center;
        }
        .k-form {
            width: 400px;
        }

    `],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {
    public form: FormGroup = new FormGroup({
        firstName: new FormControl(),
        lastName: new FormControl(),
        email: new FormControl('', Validators.email),
        acceptNews: new FormControl()
    });

    public submitForm(): void {
        this.form.markAllAsTouched();
    }

    public clearForm(): void {
        this.form.reset();
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

Form Buttons

By default, the buttons are aligned to the left side of the form. Adding the k-buttons-end class to the buttons wrapper element allows positioning them on the right.

import { Component, ViewEncapsulation } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
    selector: 'my-app',
    template: `
        <div class="example">
            <form class="k-form" [formGroup]="form">
                <fieldset class="k-form-fieldset">
                    <legend class="k-form-legend">Your Account</legend>
                    <kendo-formfield>
                        <kendo-label [for]="userName" text="Username"></kendo-label>
                        <input [formControlName]="'userName'" kendoTextBox #userName required/>

                        <kendo-formhint>Hint: Your username</kendo-formhint>
                        <kendo-formerror>Error: UserName is required</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <kendo-label [for]="email" text="Email" [optional]="true"></kendo-label>
                        <input [formControlName]="'email'" kendoTextBox #email />

                        <kendo-formhint>Hint: Your active email</kendo-formhint>
                        <kendo-formerror *ngIf="form.controls.email.errors?.required">Error: Email is required</kendo-formerror>
                        <kendo-formerror *ngIf="form.controls.email.errors?.email">Error: Not valid email format</kendo-formerror>
                    </kendo-formfield>

                    <kendo-formfield>
                        <div class="k-checkbox-wrap">
                            <input #acceptNews type="checkbox" kendoCheckBox [formControlName]="'acceptNews'"/>
                            <kendo-label [for]="acceptNews" class="k-checkbox-label" [text]="'I want to receive notifications'" [optional]="true"></kendo-label>
                        </div>

                        <kendo-formhint>You will receive our latest updates and promotions on your email</kendo-formhint>
                    </kendo-formfield>

                    <div class="k-form-buttons k-buttons-end">
                        <button class="k-button k-primary" (click)="submitForm()">Submit</button>
                        <button class="k-button" (click)="clearForm()">Clear</button>
                    </div>
                </fieldset>
            </form>
        </div>
    `,
    styles: [`
        .example {
            display: flex;
            justify-content: center;
        }
        .k-form {
            width: 400px;
        }

    `],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {
    public form: FormGroup = new FormGroup({
        userName: new FormControl(),
        lastName: new FormControl(),
        email: new FormControl('', [Validators.required, Validators.email]),
        acceptNews: new FormControl()
    });

    public submitForm(): void {
        this.form.markAllAsTouched();
    }

    public clearForm(): void {
        this.form.reset();
    }
}
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { LabelModule } from '@progress/kendo-angular-label';
import { InputsModule } from '@progress/kendo-angular-inputs';
import { AppComponent } from './app.component';

@NgModule({
    bootstrap:    [AppComponent],
    declarations: [AppComponent],
    imports:      [
        BrowserModule,
        BrowserAnimationsModule,
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        InputsModule,
        LabelModule
    ]
})
export class AppModule { }
import { enableProdMode, NgModule } from '@angular/core';
import { AppModule } from './app.module.ts';

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

In this article