In-Memory DataSource

We name an In-Memory data source when you provide your data set in one go.

Providing a data set in one go means that the DikeGrid receives the data in any of the following ways:

When you provide your data in one go, the DataGrid manages the data in memory during all the execution.

Since you can change the datasource property at runtime, consider the following code snippet:

<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onArrayDataSource()">Array
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onDikeGridDataSource()">DikeGridDataSource
    </button>
    
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onObservableDataSource()">Observable
    </button>
</div>

<dike-grid id="grid-in-memory-datasource" height="650px" #grid="dkgGrid">
</dike-grid>

With the previous UI, you can do the following actions:

  1. Array button. It creates an array of five entries.

  2. Observable button. It makes a call to an HTTP get. Then, it retrieves 500 entries from the REST API.

  3. 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.

Let us define a custom decorator:

custom-datasource-decorator.ts
import { map, tap } from 'rxjs/operators';

import { DikeDataSource } from '@dikesoft/angular-data-grid';
import { DikeDecoratorDataSource } from '@dikesoft/angular-data-grid';
import { DikeGridDataRowEntry } from '@dikesoft/angular-data-grid';

import { Employee } from 'app/mock-api/common/employees/data.model';

export class CustomDecoratorDataSource extends DikeDecoratorDataSource<Employee> {

  constructor(
    dikeDataSource: DikeDataSource<DikeGridDataRowEntry<Employee>>) {

    super(dikeDataSource);
    this.setChangeSubscription();
  }

  protected setChangeSubscription(): void {
    // As a wrapper, get the previous result stream:
    const dataStream$ = this.dikeDataSource.connect().pipe(
      tap(rows => console.log('tap-CustomDecoratorDataSource: ', rows)),
      map((rows) => {
        rows.forEach((row) => {
          let finalEmployeeId = '';
          const employeeIdSplit = 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);
  }
}

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 = new CustomDecoratorDataSource(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.

Complete code for this section

<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onArrayDataSource()">Array
    </button>

    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onDikeGridDataSource()">DikeGridDataSource
    </button>
    
    <button mat-raised-button
        class="flex-none w-56 my-2"
        color="primary"
        (click)="onObservableDataSource()">Observable
    </button>
</div>

<dike-grid id="grid-in-memory-datasource" height="650px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (dataDeliveryIdChange)="onDataDeliveryIdChange($event)"

    allowRowGrouping
    [allowColumnDragging]="gridProperties.allowColumnDragging"
    [allowRowFiltering]="gridProperties.allowRowFilter"
    [allowSorting]="gridProperties.allowSorting"
    [allowPagination]="gridProperties.allowPagination"
    [pageSize]="gridProperties.pageSize"

    allowEdition
    [editionMode]="gridProperties.editionMode"

    [decoratorFn]="dkgDecoratorFn"
    [datasource]="dkgDataSource">
    
    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350"
        sortable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="country"
        headerText="Country"
        dataType="Text"
        width="250"
        sortable>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name">

        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            sortable
            editable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            sortable
            editable>
        </dike-grid-column>

    </dike-grid-column>
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        sortable
        groupable
        draggable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        sortable
        editable
        groupable
        draggable>
    </dike-grid-column>
    
    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300"
        sortable
        editable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        sortable>
    </dike-grid-column>

</dike-grid>
  

Last updated