Search results

Observables

An Observable is used extensively by Angular since it provide significant benefits over techniques for event handling, asynchronous programming, and handling multiple values.

Observable binding using Async pipe

Grid data can be consumed from an Observable object by piping it through an async pipe. The async pipe is used to subscribe the observable object and resolve with the latest value emitted by it.

Data binding

The grid expects an object from the Observable. The emitted value should be an object with properties result and count.

import { Component, OnInit, Inject, ViewChild } from '@angular/core';
import { GridComponent } from '@syncfusion/ej2-angular-grids';
import { DataStateChangeEventArgs, Sorts, DataResult } from '@syncfusion/ej2-angular-grids'
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: `<ejs-grid [dataSource]='data | async'>
        <e-columns>
            <e-column field='id' headerText='Customer ID' width='120' textAlign='Right' isPrimaryKey='true'></e-column>
            <e-column field= "name" headerText="Customer Name" width="150"></e-column>
            <e-column field= "Freight" headerText="Freight" width="150"></e-column>
        </e-columns>
    </ejs-grid>`
})
export class AppComponent implements OnInit {
  title = 'app';
  @ViewChild('grid') private grid: GridComponent;
  public data: Object;

  constructor(@Inject(DataService) private service: DataService) {
    this.data = service;
  }

  ngOnInit() {
    let state = { skip: 0, take: 12 };
    this.service.execute(state);
  }
}
import { DataManager, Query, } from '@syncfusion/ej2-data';
import { Http } from '@angular/http';
import { Injectable } from '@angular/core';
import { DataStateChangeEventArgs } from '@syncfusion/ej2-angular-grids'
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class DataService extends Subject<Object> {

  private BASE_URL = 'https://js.syncfusion.com/demos/ejServices/Wcf/Northwind.svc/Orders';
  private dataManager = new DataManager({
    url: this.BASE_URL,
    crossDomain: true
  });

  constructor(private http: Http) {
    super();
  }

  public getData(state: DataStateChangeEventArgs): Observable<DataStateChangeEventArgs> {
    return this.http
      .get(`${this.BASE_URL}?&$inlinecount=allpages&$format=json`)
      .map((response: any) => response.json())
      .map((response: any) => (<DataResult>{
                result: response['d']['results'],
                count: parseInt(response['d']['__count'], 10)
            }))
      .map((data: any) => data);
  }
  public execute(state: DataStateChangeEventArgs): void {
    this.getData(state).subscribe(x => {
      super.next(x)
    });
  }
}

You should maintain the same Observable instance for every grid actions.

Handling grid actions

For grid actions such as paging, grouping, sorting, etc., the dataStateChange event is invoked. You have to query and resolve data using Observable in this event based on the state arguments.

import { Component, OnInit, Inject, ViewChild } from '@angular/core';
import { GridComponent } from '@syncfusion/ej2-angular-grids';
import { DataStateChangeEventArgs } from '@syncfusion/ej2-angular-grids'
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: `<ejs-grid [dataSource]='data | async' [allowPaging]='true' [allowFiltering]='true' (dataStateChange)= 'dataStateChange($event)'>
        <e-columns>
            <e-column field='id' headerText='Customer ID' width='120' textAlign='Right' isPrimaryKey='true'></e-column>
            <e-column field= "name" headerText="Customer Name" width="150"></e-column>
            <e-column field= "Freight" headerText="Freight"width="150"></e-column>
        </e-columns>
    </ejs-grid>`
})
export class AppComponent implements OnInit {
  title = 'app';
  @ViewChild('grid') private grid: GridComponent;
  public data: Object;
  public state: DataStateChangeEventArgs;
  public pageOptions: any;

  constructor(@Inject(DataService) private service: DataService) {
    this.data = service;
  }

  public dataStateChange(state: DataStateChangeEventArgs): void {
    this.service.execute(state);
  }

  ngOnInit() {
    this.pageOptions = { pageCount: 4 };
    let state = { skip: 0, take: 12 };
    this.service.execute(state);
  }
}

When initial rendering, the dataStateChange event will not be triggered. You can perform the operation in the ngOnInit if you want the grid to show the record.

Perform CRUD operations

The dataSourceChanged event is triggered to update the grid data. You can perform the save operation based on the event arguments and you need to call the endEdit method to indicate the completion of save operation.

To learn about how to perform CRUD operation with observable data to the Angular Grid, you can check on this video

import { Component, OnInit, Inject } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';
import { DataService } from './crud.service';
import { Customer } from './customer';
import { DataStateChangeEventArgs, Sorts, DataSourceChangedEventArgs } from '@syncfusion/ej2-grids';
@Component({
    selector: 'app-container',
    template: `<ejs-grid [dataSource]='data | async' [editSettings]='editSettings' [toolbar]='toolbar' (dataSourceChanged)='dataSourceChanged($event)' (dataStateChange)= 'dataStateChange($event)'>
        <e-columns>
            <e-column field='id' headerText='Customer ID' width='120' textAlign='Right' isPrimaryKey='true'></e-column>
            <e-column field= "name" headerText="Customer Name" width="150"></e-column>
            <e-column field= "Freight" headerText="Freight" width="150"></e-column>
        </e-columns>
    </ejs-grid>`
})
export class AppComponent {
    public data: Observable<DataStateChangeEventArgs>;;
    public editSettings: Object;
    public toolbar: string[];
    public state: DataStateChangeEventArgs;
    customers: Customer[];
    constructor(private dataService: DataService) {
        this.data = dataService;
    }

    public dataStateChange(state: DataStateChangeEventArgs): void {
        this.dataService.execute(state);
    }

    public dataSourceChanged(state: DataSourceChangedEventArgs): void {
        if (state.action === 'add') {
            this.dataService.addRecord(state).subscribe(() => state.endEdit());
        } else if (state.action === 'edit') {
            this.dataService.updateRecord(state).subscribe(() => state.endEdit());
        } else if (state.requestType === 'delete') {
            this.dataService.deleteRecord(state).subscribe(() => state.endEdit());
        }
    }

    public ngOnInit(): void {
        this.editSettings = { allowEditing: true, allowAdding: true, allowDeleting: true};
        this.toolbar = ['Add', 'Edit', 'Delete', 'Update', 'Cancel'];
        let state = { skip: 0, take: 12 };
        this.dataService.execute(state);
    }
}

Calculate aggregates

The footer aggregate values should be calculated and sent along with the dataSource property as follows. The aggregate property of the data source should contain the aggregate value assigned to the field – type property. For example, the Sum aggregate value for the Freight field should be assigned to the Freight - sum property.

{
    result: [{..}, {..}, {..}, ...],
    count: 830,
    aggregates: { 'Freight - sum' : 450,'EmployeeID - min': 1 }
}

The group footer and caption aggregate values can be calculated by the grid itself.

Provide Excel Filter data source

The dataStateChange event is triggered with appropriate arguments when the Excel filter requests the filter choice data source. You need to resolve the Excel filter data source using the dataSource resolver function from the state argument as follows.

import { Component, OnInit, Inject, ViewChild } from '@angular/core';
import { SortSettingsModel, FilterSettingsModel, ColumnModel, ToolbarItems, ToolbarItem, GroupSettingsModel } from '@syncfusion/ej2-grids';
import { GridComponent } from '@syncfusion/ej2-angular-grids';
import { DataStateChangeEventArgs, Sorts, DataResult, DataSourceChangedEventArgs } from '@syncfusion/ej2-angular-grids'
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: `<ejs-grid #grid [dataSource]='data | async'  [filterSettings]='filterSettings' [allowFiltering]='true' (dataStateChange)='dataStateChange($event)'>
    <e-columns>
        <e-column field="OrderID" headerText="Order ID" width="130" isPrimaryKey='true'></e-column>
        <e-column field="CustomerID" headerText="Customer ID" width="150"></e-column>
        <e-column field='Freight' headerText='Freight' width='120' textAlign='Right'></e-column>
        <e-column field="ShipCity" headerText="ShipCity" width="150"></e-column>
    </e-columns>
</ejs-grid>`
})
export class AppComponent implements OnInit {
  title = 'app';
  @ViewChild('grid') private grid: GridComponent;
  public data: Object;
  public state: DataStateChangeEventArgs;
  public pageOptions: any;
  public filterSettings: Object;

  constructor(@Inject(DataService) private service: DataService) {
    this.data = service;
  }
   public dataStateChange(state: DataStateChangeEventArgs): void {
    if (state.action.requestType === "filterchoicerequest" || state.action.requestType ==="filtersearchbegin") {
      this.service.getData(state).subscribe((e) => state.dataSource(e))
    } else {
      this.service.execute(state);
    }
  }
  ngOnInit() {
    this.pageOptions = { pageCount: 4 };
    this.filterSettings = { type: 'Excel' };
    let state = { skip: 0, take: 12 };
    this.service.execute(state);
  }
}

Using Observable without async pipe

The async pipe helps you to auto subscribe the Observable. If you are not comfortable with using async then just subscribe the Observable and resolve the view data manually.

import { Component, OnInit, Inject, ViewChild } from '@angular/core';
import { GridComponent } from '@syncfusion/ej2-angular-grids';
import { DataStateChangeEventArgs} from '@syncfusion/ej2-angular-grids'
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: `<ejs-grid [dataSource]='data' [allowPaging]='true' [allowFiltering]='true' (dataStateChange)= 'dataStateChange($event)'>
        <e-columns>
            <e-column field='id' headerText='Customer ID' width='120' textAlign='Right' isPrimaryKey='true'></e-column>
            <e-column field= "name" headerText="Customer Name" width="150"></e-column>
            <e-column field= "Freight" headerText="Freight" width="150"></e-column>
        </e-columns>
    </ejs-grid>`
})
export class AppComponent implements OnInit {
  title = 'app';
  @ViewChild('grid') private grid: GridComponent;
  public data: Object;
  public state: DataStateChangeEventArgs;
  public pageOptions: any;
  public filterSettings: Object;

  constructor(@Inject(DataService) private service: DataService) {
    this.data = service;
  }

   public dataStateChange(state: DataStateChangeEventArgs): void {
    this.service.getData(state).subscribe((response) => this.data = response);
  }
  ngOnInit() {
    let state = { skip: 0, take: 12};
    this.service.getData(state).subscribe((response)=> this.data = response);
  }
}