All Components

External Form Editing

The Grid provides options for editing its data by building external popup editing forms.

Setup

To build external popup editing forms in the Kendo UI Grid for Angular, utilize the Reactive Forms and the Dialog component, as demonstrated in the following example.

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Validators, FormGroup, FormControl } from '@angular/forms';
import { Product } from './model';

@Component({
    selector: 'kendo-grid-edit-form',
    styles: [
      "input[type=text] { width: 100%; }"
    ],
    template: `
        <kendo-dialog *ngIf="active" (close)="closeForm()">
          <kendo-dialog-titlebar>
            {{ isNew ? 'Add new product' : 'Edit product' }}
          </kendo-dialog-titlebar>

            <form novalidate [formGroup]="editForm">
                <div class="form-group">
                    <label for="ProductName" class="control-label">Product name</label>

                    <input type="text" class="k-textbox" formControlName="ProductName" />

                    <div class="k-tooltip k-tooltip-validation" [hidden]="editForm.controls.ProductName.valid || editForm.controls.ProductName.pristine">
                        ProductName is required
                    </div>
                </div>
                <div class="form-group">
                    <label for="UnitPrice" class="control-label">Unit price</label>

                    <input type="text" class="k-textbox" formControlName="UnitPrice" />
                </div>
                <div class="form-group">
                    <label for="UnitsInStock" class="control-label">Units in stock</label>

                    <input type="text" class="k-textbox" formControlName="UnitsInStock" />

                    <div class="k-tooltip k-tooltip-validation" [hidden]="editForm.controls.UnitsInStock.valid || editForm.controls.UnitsInStock.pristine">
                        Units must be between 0 and 99
                    </div>
                </div>
                <div class="form-group">
                    <label>
                      <input type="checkbox" formControlName="Discontinued" />
                      Discontinued product
                    </label>
                </div>
            </form>

            <kendo-dialog-actions>
                <button class="k-button" (click)="onCancel($event)">Cancel</button>
                <button class="k-button k-primary" [disabled]="!editForm.valid" (click)="onSave($event)">Save</button>
            </kendo-dialog-actions>
        </kendo-dialog>
    `
})
export class GridEditFormComponent {
    private editForm = new FormGroup({
        'ProductID': new FormControl(),
        'ProductName': new FormControl("", Validators.required),
        'UnitPrice': new FormControl(0),
        'UnitsInStock': new FormControl("", Validators.compose([Validators.required, Validators.pattern('^[0-9]{1,2}')])),
        'Discontinued': new FormControl(false)
    });

    private active: boolean = false;
    @Input() public isNew: boolean = false;

    @Input() public set model(product: Product) {
        this.editForm.reset(product);

        this.active = product !== undefined;
    }

    @Output() cancel: EventEmitter<any> = new EventEmitter();
    @Output() save: EventEmitter<Product> = new EventEmitter();

    public onSave(e): void {
        e.preventDefault();
        this.save.emit(this.editForm.value);
        this.active = false;
    }

    public onCancel(e): void {
        e.preventDefault();
        this.closeForm();
    }

    private closeForm(): void {
        this.active = false;
        this.cancel.emit();
    }
}
export class Product {
    public ProductID: number;
    public ProductName: string = "";
    public Discountinued: boolean = false;
    public UnitsInStock: number;
    public UnitPrice: number = 0;
}
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Jsonp } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

const CREATE_ACTION = 'create';
const UPDATE_ACTION = 'update';
const REMOVE_ACTION = 'destroy';

@Injectable()
export class EditService extends BehaviorSubject<any[]> {
    constructor(private jsonp: Jsonp) {
        super([]);
    }

    private data: any[] = [];

    public read() {
        if (this.data.length) {
            return super.next(this.data);
        }

        this.fetch()
            .do(data => this.data = data)
            .subscribe(data => {
                super.next(data);
            });
    }

    public save(data: any, isNew?: boolean) {
        const action = isNew ? CREATE_ACTION : UPDATE_ACTION;

        this.reset();

        this.fetch(action, data)
            .subscribe(() => this.read(), () => this.read());
    }

    public remove(data: any) {
        this.reset();

        this.fetch(REMOVE_ACTION, data)
            .subscribe(() => this.read(), () => this.read());
    }

    public resetItem(dataItem: any) {
        if (!dataItem) { return; }

        //find orignal data item
        const originalDataItem = this.data.find(item => item.ProductID === dataItem.ProductID);

        //revert changes
        Object.assign(originalDataItem, dataItem);

        super.next(this.data);
    }

    private reset() {
        this.data = [];
    }

    private fetch(action: string = "", data?: any): Observable<any[]>  {
        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?: any): string {
       return data ? `&models=${JSON.stringify([data])}` : '';
    }
}
import { Observable } from 'rxjs/Rx';
import { Component, OnInit, Inject } from '@angular/core';

import { GridDataResult } from '@progress/kendo-angular-grid';
import { State, process } from '@progress/kendo-data-query';

import { Product } from './model';
import { EditService } from './edit.service';

@Component({
  selector: 'my-app',
  template: `
      <kendo-grid
          [data]="view | async"
          [height]="533"
          [pageSize]="gridState.take" [skip]="gridState.skip" [sort]="gridState.sort"
          [pageable]="true" [sortable]="true"
          (dataStateChange)="onStateChange($event)"
          (edit)="editHandler($event)" (remove)="removeHandler($event)"
          (add)="addHandler($event)"
        >
        <ng-template kendoGridToolbarTemplate>
            <button kendoGridAddCommand>Add new</button>
        </ng-template>
        <kendo-grid-column field="ProductName" title="Product Name"></kendo-grid-column>
        <kendo-grid-column field="UnitPrice" title="Price"></kendo-grid-column>
        <kendo-grid-column field="Discontinued" title="Discontinued"></kendo-grid-column>
        <kendo-grid-column field="UnitsInStock" title="Units In Stock"></kendo-grid-column>
        <kendo-grid-command-column title="command" width="220">
            <ng-template kendoGridCellTemplate>
                <button kendoGridEditCommand class="k-primary">Edit</button>
                <button kendoGridRemoveCommand>Delete</button>
            </ng-template>
        </kendo-grid-command-column>
      </kendo-grid>

      <kendo-grid-edit-form [model]="editDataItem" [isNew]="isNew"
          (save)="saveHandler($event)"
          (cancel)="cancelHandler()">
      </kendo-grid-edit-form>
  `
})
export class AppComponent implements OnInit {
    public view: Observable<GridDataResult>;
    public gridState: State = {
        sort: [],
        skip: 0,
        take: 10
    };

    private editService: EditService;
    private editDataItem: Product;
    private isNew: boolean;

    constructor(@Inject(EditService) editServiceFactory: any) {
        this.editService = editServiceFactory();
    }

    public ngOnInit(): void {
        this.view = this.editService.map(data => process(data, this.gridState));

        this.editService.read();
    }

    public onStateChange(state: State) {
        this.gridState = state;

        this.editService.read();
    }

    public addHandler() {
        this.editDataItem = new Product();
        this.isNew = true;
    }

    public editHandler({dataItem}) {
        this.editDataItem = dataItem;
        this.isNew = false;
    }

    public cancelHandler() {
        this.editDataItem = undefined;
    }

    public saveHandler(product: Product) {
        this.editService.save(product, this.isNew);

        this.editDataItem = undefined;
    }

    public removeHandler({dataItem}) {
        this.editService.remove(dataItem);
    }
}
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Jsonp, JsonpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';

import { DialogModule } from '@progress/kendo-angular-dialog';
import { GridModule } from '@progress/kendo-angular-grid';

import { AppComponent } from './app.component';
import { GridEditFormComponent }  from './edit-form.component';
import { EditService } from './edit.service';

@NgModule({
    declarations: [
        GridEditFormComponent,
        AppComponent
    ],
    imports: [
        JsonpModule,
        BrowserModule,
        BrowserAnimationsModule,
        ReactiveFormsModule,
        GridModule,
        DialogModule
    ],
    providers: [
        {
            deps: [Jsonp],
            provide: EditService,
            useFactory: (jsonp: Jsonp) => () => new EditService(jsonp)
        }
    ],
    bootstrap: [AppComponent]
})
export class AppModule {}

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './ng.module';

enableProdMode();

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
In this article