onArrayDataSource(): void {// Create an array of five entries:this.dkgDataSource = [{ employeeId:'43c8a235-a95d-4e3e-bc30-f31ff33d490d', firstName:'Dolores', lastName:'Zieme', gender:'female', age:51, email:'dolores.zieme@gmail.com', hireDate:newDate(2020,7,6), country:'Mexico' }, { employeeId:'0f1bc4f3-f746-489a-9c24-eb570a16e556', firstName:'Celia', lastName:'Mertz', gender:'female', age:55, email:'celia.mertz@gmail.com', hireDate:newDate(2010,5,11), country:'Netherlands' }, { employeeId:'88acf950-dee5-480a-a67e-1b0419c9a473', firstName:'Jeffery', lastName:'Renner', gender:'male', age:60, email:'jeffery.renner@hotmail.com', hireDate:newDate(2016,11,22), country:'Bulgaria' }, { employeeId:'03a4e13f-98b4-4998-a40b-b66a832f327e', firstName:'Annette', lastName:'Satterfield', gender:'female', age:37, email:'annette.satterfield@yahoo.com', hireDate:newDate(2019,5,2), country:'Germany' }, { employeeId:'a800045a-5127-4a5f-b93f-635f162ad376', firstName:'Geraldine', lastName:'Weimann', gender:'female', age:42, email:'annette.satterfield@yahoo.com', hireDate:newDate(2016,10,19), country:'United States of America' }] asPartial<Employee>[];}onDikeGridDataSource(): void {// Create a DikeGridDataSource object:constgridDataSource=newDikeGridDataSource<Employee>();// Take 2000 entries from the REST service:this.sampleData.getEmployees(2000).subscribe(employees =>gridDataSource.entries = employees);// Assign the new DikeGridDataSource dataSource:this.dkgDataSource = gridDataSource;}onObservableDataSource(): void {// Take 500 entries from the REST service:this.dkgDataSource =this.sampleData.getEmployees(500);}
With the previous UI, you can do the following actions:
Array button. It creates an array of five entries.
Observable button. It makes a call to an HTTP get. Then, it retrieves 500 entries from the REST API.
DikeGridDataSource button. It creates a DikeGridDataSource instance. Then it makes a call to an HTTP get. We assign the response to the entries property from the DikeGridDataSource.
You can see how the unique id changes every time the datasource property changes.
DataSource Decorators
Once you assign your data source to the DikeGrid instance, it takes those rows into a pipe of operations.
See the following diagram:
The DikeGridDataSource instance is always the first instance in the chain.
As you can see, the DikeGrid creates the following instances:
We based on the Decorator pattern to design the previous chain of operations. That is why we named every instance as a decorator. Every decorator wraps the last decorator instance.
This pattern gives us great flexibility in adding or removing operations to the chain and encapsulating responsibilities in each decorator.
The DikeGrid will set the DikePagingDataSource decorator at the end of the chain of operations or after the created tree due to the row grouping operation.
Custom Decorators
You can create your decorators and add them to the chain of operations. To create a decorator, you have to extend from the abstract class DikeDecoratorDataSource.
In the previous code, we only hide the Employee Id value. We intentionally left a tap RxJS operator to verify that the DikeGrid invokes the custom decorator.
Once you have defined your decorators, you can add them to the chain of operations, implementing the function DataSourceDecoratorFn. Then, you will assign it to the input property named decoratorFn.
Be aware that you can assign the decoratorFn property only at the initialization phase.
Let us assign the decoratorFn property.
in-memory-data-source.component.ts
ngOnInit(): void {//...// We create our chain of operations implementing the DataSourceDecoratorFn function: this.dkgDecoratorFn = (dataSource: DikeGridDataSource<Employee>, dikeGrid: DikeGridComponent<Employee>): DikeDataSource<DikeGridDataRowEntry<Employee>> => {
// Firstly, point to the given DikeGridDataSource<Employee>, then add the decorators:let dikeDecoratorDataSource:DikeDataSource<DikeGridDataRowEntry<Employee>> = dataSource;// Custom decorator: dikeDecoratorDataSource =newCustomDecoratorDataSource(dikeDecoratorDataSource);// Filtering decorator: dikeDecoratorDataSource =dikeGrid.dataSourceFactory.createFilteringDecorator(dikeDecoratorDataSource);// Sorting decorator: dikeDecoratorDataSource =dikeGrid.dataSourceFactory.createSortingDecorator(dikeDecoratorDataSource);return dikeDecoratorDataSource; };}
As you can see, you have access to the property named dataSourceFactory under the DikeGrid instance.
The dataSourceFactory property is of type DikeGridFactoryDataSource. It is a read-only property used to create filtering and sorting decorators.
Decorators and Row Grouping operation
When the user groups rows, the DikeGrid internally creates a tree where every node represents a group of data rows. Only the leaves have the actual data rows.
According to the previous statement, the DikeGrid takes the defined chain of operations and sets it to every tree leaf.
Summary
This type of DataSource receives the data in one go. Therefore, the DikeGrid manages the provided data in memory during all execution. Furthermore, DikeGrid uses the Decorator pattern to create a chain of operations. Thus the DikeGrid handles, for instance, filtering and sorting processes, among others.
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Subscription } from'rxjs';import { DataSourceDecoratorFn, DikeDataSource, DikeGridComponent, DikeGridDataRowEntry, DikeGridDataSource, DikeGridDataSourceInput } from'@dikesoft/angular-data-grid';import { DikeGridProperties } from'app/core/config/dike-grid.properties';import { Employee } from'app/mock-api/common/employees/data.model';import { CustomDecoratorDataSource } from'./custom-datasource-decorator';import { DikeGridConfig } from'app/services/dike-grid.config.service';import { SampleData } from'app/services/sample-data.service';@Component({ selector:'in-memory-data-source', templateUrl:'./in-memory-data-source.component.html', styleUrls: ['./in-memory-data-source.component.scss'], encapsulation:ViewEncapsulation.None, changeDetection:ChangeDetectionStrategy.OnPush})exportclassInMemoryDataSourceComponentimplementsOnInit,OnDestroy { dkgDecoratorFn:DataSourceDecoratorFn<Employee>; dkgDataSource:DikeGridDataSourceInput<Partial<Employee>>; gridProperties:DikeGridProperties;private changeGridPropertiesSubscription:Subscription=Subscription.EMPTY;constructor(private cdr:ChangeDetectorRef,private gridConfig:DikeGridConfig,private sampleData:SampleData) { }ngOnInit():void {// Listening to any config property change:this.setChangeGridPropertiesSubscription();// Assign 1000 entries from the REST API:this.dkgDataSource =this.sampleData.getEmployees(1000);// We create our chain of operations implementing the DataSourceDecoratorFn function: this.dkgDecoratorFn = (dataSource: DikeGridDataSource<Employee>, dikeGrid: DikeGridComponent<Employee>): DikeDataSource<DikeGridDataRowEntry<Employee>> => {
// Firstly, point to the given DikeGridDataSource<Employee>, then add the decorators:let dikeDecoratorDataSource:DikeDataSource<DikeGridDataRowEntry<Employee>> = dataSource;// Custom decorator: dikeDecoratorDataSource =newCustomDecoratorDataSource(dikeDecoratorDataSource);// Filtering decorator: dikeDecoratorDataSource =dikeGrid.dataSourceFactory.createFilteringDecorator(dikeDecoratorDataSource);// Sorting decorator: dikeDecoratorDataSource =dikeGrid.dataSourceFactory.createSortingDecorator(dikeDecoratorDataSource);return dikeDecoratorDataSource; }; }ngOnDestroy(): void {this.changeGridPropertiesSubscription.unsubscribe(); }onDataDeliveryIdChange(id: string): void {console.log(`Data delivery id: ${id}`); }onArrayDataSource(): void {// Create an array of five entries:this.dkgDataSource = [{ employeeId:'43c8a235-a95d-4e3e-bc30-f31ff33d490d', firstName:'Dolores', lastName:'Zieme', gender:'female', age:51, email:'dolores.zieme@gmail.com', hireDate:newDate(2020,7,6), country:'Mexico' }, { employeeId:'0f1bc4f3-f746-489a-9c24-eb570a16e556', firstName:'Celia', lastName:'Mertz', gender:'female', age:55, email:'celia.mertz@gmail.com', hireDate:newDate(2010,5,11), country:'Netherlands' }, { employeeId:'88acf950-dee5-480a-a67e-1b0419c9a473', firstName:'Jeffery', lastName:'Renner', gender:'male', age:60, email:'jeffery.renner@hotmail.com', hireDate:newDate(2016,11,22), country:'Bulgaria' }, { employeeId:'03a4e13f-98b4-4998-a40b-b66a832f327e', firstName:'Annette', lastName:'Satterfield', gender:'female', age:37, email:'annette.satterfield@yahoo.com', hireDate:newDate(2019,5,2), country:'Germany' }, { employeeId:'a800045a-5127-4a5f-b93f-635f162ad376', firstName:'Geraldine', lastName:'Weimann', gender:'female', age:42, email:'annette.satterfield@yahoo.com', hireDate:newDate(2016,10,19), country:'United States of America' }] asPartial<Employee>[]; }onDikeGridDataSource(): void {// Create a DikeGridDataSource object:constgridDataSource=newDikeGridDataSource<Employee>();// Take 2000 entries from the REST API:this.sampleData.getEmployees(2000).subscribe(employees =>gridDataSource.entries = employees);// Assign the new DikeGridDataSource dataSource:this.dkgDataSource = gridDataSource; }onObservableDataSource(): void {// Take 500 entries from the REST API:this.dkgDataSource =this.sampleData.getEmployees(500); } private setChangeGridPropertiesSubscription(): void {this.changeGridPropertiesSubscription.unsubscribe();this.changeGridPropertiesSubscription =this.gridConfig.configChange.subscribe((props:DikeGridProperties) => {this.gridProperties = props;this.cdr.markForCheck(); }); }}
import { map, tap } from'rxjs/operators';import { DikeDataSource, DikeDecoratorDataSource, DikeGridDataRowEntry } from'@dikesoft/angular-data-grid';import { Employee } from'app/mock-api/common/employees/data.model';exportclassCustomDecoratorDataSourceextendsDikeDecoratorDataSource<Employee> {constructor( dikeDataSource:DikeDataSource<DikeGridDataRowEntry<Employee>>) {super(dikeDataSource);this.setChangeSubscription(); }protectedsetChangeSubscription():void {// As a wrapper, get the previous result stream:constdataStream$=this.dikeDataSource.connect().pipe(tap(rows =>console.log('tap-CustomDecoratorDataSource: ', rows)),map((rows) => {rows.forEach((row) => {let finalEmployeeId ='';constemployeeIdSplit=row.entry.employeeId.split('-');for (let i=0; i<employeeIdSplit.length-1; i++) { finalEmployeeId +='x'.repeat(employeeIdSplit[i].length) +'-'; }row.entry.employeeId = finalEmployeeId + employeeIdSplit[employeeIdSplit.length-1]; });return rows; }) );this.changeSubscription.unsubscribe();this.changeSubscription =dataStream$.subscribe(rows =>this.data = rows); }}
Decorator class
Description
Array
When you directly assign a variety of entries.
Observable<Array>
This type of data usually comes from an HTTP get invocation.
DikeGridDataSource
You can create an instance of DikeGridDataSource, then assign your data to the entries property.
DikeRefreshingDataSource
This decorator always wraps a DikeGridDataSource instance. It pushes the rows when the user has updated them. The DikeGrid creates this decorator if you have allowed the edition operation.
DikeEditionStateDataSource
It splits the set of rows depending on their status, Modified, Deleted, or Editing. The DikeGrid creates this decorator if you have allowed the edition operation.
DikeFilteringDataSource
This decorator manages all the filtering operations. Even the filters applied to the rows in edition mode.
DikeSortingDataSource
This decorator manages all the sorting operations.