All Components

Working with Custom Values

By default, when the Enter key is pressed or after the component loses focus, values that do not appear in the supplied list of items are dismissed.

To configure the ComboBox to accept custom values, set the allowCustom property to true.

Handle Custom Primitive Values

If the component is bound to a primitive value, you only have to set the allowCustom property to true.

This scenario is valid when the component is either bound to a dataset of primitive values or to a dataset of objects, and the valuePrimitive property is set to true.

The following example demonstrates how to allow custom primitive values.

@Component({
  selector: 'my-app',
  template: `
    <p>Custom values are <strong>enabled</strong>. Type a custom value.</p>

    <p>primitive data</p>
    <kendo-combobox
        [data]="sizes"
        [value]="selectedSize"
        [allowCustom]="true"
        (valueChange)="onSizeChange($event)"
    >
    </kendo-combobox>
  `
})
class AppComponent {
    public sizes: Array<string> = [ "Small", "Medium", "Large" ];
    public selectedSize: string = "Medium";

    public onSizeChange(value) {
        this.selectedSize = value;
    }
}

Handle Custom Object Values

If the component is bound to objects, you have to set the allowCustom property to true and to provide a valueNormalizer function. Its purpose is to convert the user text input into an object that can be recognized as a valid ComboBox value.

The valueNormalizer function receives Observable<string> as an argument and is expected to return a single normalized value wrapped as Observable that will be further processed by the component. For more information check the comments in the examples below.

@Component({
  selector: 'my-app',
  template: `
    <p>Custom values are <strong>enabled</strong>. Type a custom value.</p>
    <p>ComboBox value: {{ size|json }}</p>

    <kendo-combobox
        [allowCustom]="true"
        [data]="listItems"
        [textField]="'text'"
        [valueField]="'value'"
        [valueNormalizer]="valueNormalizer"
        [(ngModel)]="size"
    >
    </kendo-combobox>
  `
})

class AppComponent {
    public listItems: Array<{ text: string, value: number }> = [
        { text: "Small", value: 1 },
        { text: "Medium", value: 2 },
        { text: "Large", value: 3 }
    ];

    public size: { text: string, value: number } = { text: "Medium", value: 2 };

    /*
        The component will emit custom text by the user and pass it to the `valueNormalizer` method.
        The developer can process the custom text, create single custom object that represents the normalized value
        and pass it back wrapped as Observable.

        In this example we use the `map` operator to transform text into normalized value object.

        For further information about `map` operator check:
        http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-map
    */
    public valueNormalizer = (text: Observable<string>) => text.map((text: string) => {
        return {
            value: this.listItems[this.listItems.length - 1].value + 1,
            text: text
        };
    });
}

Handle Custom Object Values via remote service

import { Component, Inject } from '@angular/core';
import { Response, ResponseOptions, Http } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
import { DataService } from './data.service';
import { Product } from '../common/product.model'

@Component({
  selector: 'my-app',
  template: `
            <p>Type custom value and press enter.</p>
            <p>Value: {{item | json }}</p>
            <kendo-combobox
                [data]="listItems"
                [textField]="'ProductName'"
                [valueField]="'ProductId'"
                [(ngModel)]="item"
                [valueNormalizer]="valueNormalizer"
                [allowCustom]="true"
            >
            </kendo-combobox>`
})
export class AppComponent {
    public listItems: Product[] = [];
    public item: Product = { ProductID: 1, ProductName: 'Chai' };

    constructor(
        public http: Http,
        private backend: MockBackend,
        @Inject(DataService) private dataService: DataService
    ) {
        /*
          Mocked backend service.
          For further details check:
          https://angular.io/docs/ts/latest/api/http/testing/index/MockBackend-class.html

          *Do NOT use in production environment*
        */
        this.backend.connections.subscribe((c: any) => {
            let response = new Response(<ResponseOptions>{ status: 200 });

            if (c.request.url === "normalize/url") {
                c.mockDownload(response);
                setTimeout(() => {
                    c.mockRespond({
                        ProductId: Math.floor(Math.random() * 20) + 4 ,
                        ProductName: JSON.parse(c.request.getBody()).text
                    });
                }, 1500);
            }
        });
    }

    ngOnInit() {
        /*
            Fetch ComboBox data from remote service.
        */
        this.dataService.fetchData().subscribe(data => this.listItems = data)
    }

    /*
        Maps each custom text that the user types to the result of a function that performs http request to remove service.
        For further details about `switchMap` operator check:
        http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-switchMap
    */
    public valueNormalizer =  (text: Observable<string>): any => text.switchMap(this.service);

    /*
        Send the custom text to remote service that will process it and send back a generated data item with `ProductID` and `ProductName` fields.
        Note that the response should contain *single item* that represents the normalized value.
        The response value will be returned by the `valueNormalizer` function to the ComboBox.
        If you want to modify the server response before it is returned by the valueNormalizer to the ComboBox component, use the `map` operator:
        http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-map

        For further details how to perform a http request check:
        https://angular.io/docs/ts/latest/guide/server-communication.html

        *IMPORTANT* If the request fails due to some reason, the component will clear the custom text and will reset its value.
        If you want to notify the user for the error use catch operator.

        For further details how to handle http errors check:
        https://angular.io/docs/ts/latest/guide/server-communication.html#!#always-handle-errors
    */
    public service = (text: string): any => this.http.post('normalize/url', { text })
}
import { enableProdMode, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Http, HttpModule, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions, ResponseType, JsonpModule } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { FormsModule } from '@angular/forms';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
import { AppComponent }   from './app.component';
import { DataService }   from './data.service';

@NgModule({
  imports:      [ BrowserModule, HttpModule, DropDownsModule, FormsModule, JsonpModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ],
  providers: [BaseRequestOptions, DataService, MockBackend, {
        provide: Http,
        deps: [MockBackend, BaseRequestOptions],
        useFactory: (backend, options) => {
            return new Http(backend, options);
        }
  }]
})

export class AppModule { }

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
import { Injectable, Inject } from '@angular/core';
import { Jsonp, JsonpModule } from '@angular/http';
import { Product } from './product.model';
import { Observable } from 'rxjs/Rx';

@Injectable()

export class DataService {


  constructor(@Inject(Jsonp) private jsonp: Jsonp) { }

  fetchData(action: string = "", data?: Product): Observable<Product[]>{
    return this.jsonp.get(`http://demos.telerik.com/kendo-ui/service/Products/${action}?callback=JSONP_CALLBACK${this.serializeModels(data)}`).map(response => response.json())
  }

  private serializeModels(data?: Product): string {
    return data ? `&models=${JSON.stringify([data])}` : '';
  }

}
export class Product {
    constructor(
        public ProductID?: number,
        public ProductName?: string,
        public UnitPrice?: number,
        public UnitsInStock?: number,
        public Discontinued?: boolean
    ) { }
}

Handle errors that occur while normalizing custom object values via remote service

import { Component, Inject } from '@angular/core';
import { Response, ResponseOptions, Http } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
import { DataService } from './data.service';
import { Product } from '../common/product.model'

@Component({
  selector: 'my-app',
  template: `
            <p>This example demonstrates how to handle server errors that occur in the normalization service.</p>
            <p>Type custom value and press enter.</p>
            <p>Value: {{item | json }}</p>
            <kendo-combobox
                [data]="listItems"
                [textField]="'ProductName'"
                [valueField]="'ProductId'"
                [(ngModel)]="item"
                [valueNormalizer]="valueNormalizer"
                [allowCustom]="true"
            >
            </kendo-combobox>
            <span *ngIf="hasError" style="color: red;">{{ errorMessage }}</span>
  `
})
export class AppComponent {
    public listItems: Product[] = [];
    public item: Product = { ProductID: 1, ProductName: 'Chai' };
    public hasError: boolean = false;
    public errorMessage: string = "";

    constructor(
        public http: Http,
        private backend: MockBackend,
        @Inject(DataService) private dataService: DataService
    ) {
        /*
          Mocked backend service.
          For further details check:
          https://angular.io/docs/ts/latest/api/http/testing/index/MockBackend-class.html

          *Do NOT use in production environment*
        */
        this.backend.connections.subscribe((c: any) => {
            let response = new Response(<ResponseOptions>{ status: 500 });

            if (c.request.url === "normalize/url") {
                c.mockDownload(response);
                setTimeout(() => {
                    c.mockError({ error: "Error occured" });
                }, 1500);
            }
        });
    }

    ngOnInit() {
        /*
            Fetch ComboBox data from remote service.
        */
        this.dataService.fetchData().subscribe(data => this.listItems = data)
    }

    /*
        Maps each custom text that the user types to the result of a function that performs http request to remove service.
        For further details about `switchMap` operator check:
        http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-switchMap
    */
    public valueNormalizer =  (text: Observable<string>): any => text.switchMap(this.service);

    /*
        Send the custom text to remote service that will process it and send back a generated data item with `ProductID` and `ProductName` fields.
        Note that the response should contain *single item* that represents the normalized value.
        The response value will be returned by the `valueNormalizer` function to the ComboBox.
        If you want to modify the server response before it is returned by the valueNormalizer to the ComboBox component, use the `map` operator:
        http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-map

        For further details how to perform a http request check:
        https://angular.io/docs/ts/latest/guide/server-communication.html

        *IMPORTANT* If the request fails due to some reason, the component will clear the custom text and will reset its value.
        If you want to notify the user for the error use catch operator.

        For further details how to handle http errors check:
        https://angular.io/docs/ts/latest/guide/server-communication.html#!#always-handle-errors
    */
    public service = (text: string): any => this.http
        .post('normalize/url', { text })
        .catch((response) => {
            this.hasError = true;
            this.errorMessage = response.error;
        });
}
import { enableProdMode, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Http, HttpModule, ConnectionBackend, BaseRequestOptions, Response, ResponseOptions, ResponseType, JsonpModule } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { FormsModule } from '@angular/forms';
import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
import { AppComponent }   from './app.component';
import { DataService }   from './data.service';

@NgModule({
  imports:      [ BrowserModule, HttpModule, DropDownsModule, FormsModule, JsonpModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ],
  providers: [BaseRequestOptions, DataService, MockBackend, {
        provide: Http,
        deps: [MockBackend, BaseRequestOptions],
        useFactory: (backend, options) => {
            return new Http(backend, options);
        }
  }]
})

export class AppModule { }

enableProdMode();

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
import { Injectable, Inject } from '@angular/core';
import { Jsonp, JsonpModule } from '@angular/http';
import { Product } from './product.model';
import { Observable } from 'rxjs/Rx';

@Injectable()

export class DataService {


  constructor(@Inject(Jsonp) private jsonp: Jsonp) { }

  fetchData(action: string = "", data?: Product): Observable<Product[]>{
    return this.jsonp.get(`http://demos.telerik.com/kendo-ui/service/Products/${action}?callback=JSONP_CALLBACK${this.serializeModels(data)}`).map(response => response.json())
  }

  private serializeModels(data?: Product): string {
    return data ? `&models=${JSON.stringify([data])}` : '';
  }

}
export class Product {
    constructor(
        public ProductID?: number,
        public ProductName?: string,
        public UnitPrice?: number,
        public UnitsInStock?: number,
        public Discontinued?: boolean
    ) { }
}
In this article