# In-Memory DataSource

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

| Data set                                               | Description                                                                                                                                                               |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:green;">`Array`</mark>              | When you directly assign a variety of entries.                                                                                                                            |
| <mark style="color:green;">`Observable<Array>`</mark>  | This type of data usually comes from an `HTTP get` invocation.                                                                                                            |
| <mark style="color:green;">`DikeGridDataSource`</mark> | You can create an instance of <mark style="color:green;">`DikeGridDataSource`</mark>, then assign your data to the <mark style="color:orange;">`entries`</mark> property. |

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

Since you can change the <mark style="color:orange;">`datasource`</mark> property at runtime, consider the following code snippet:

{% tabs %}
{% tab title="in-memory-data-source.component.html" %}

```markup
<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>

```

{% endtab %}

{% tab title="in-memory-data-source.component.ts" %}

```typescript
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: new Date(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: new Date(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: new Date(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: new Date(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: new Date(2016, 10, 19),
    country: 'United States of America'
  }] as Partial<Employee>[];
}

onDikeGridDataSource(): void {
  // Create a DikeGridDataSource object:
  const gridDataSource = new DikeGridDataSource<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);
}

```

{% endtab %}
{% endtabs %}

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 <mark style="color:green;">`DikeGridDataSource`</mark> instance. Then it makes a call to an `HTTP get`. We assign the response to the <mark style="color:orange;">`entries`</mark> property from the <mark style="color:green;">`DikeGridDataSource`</mark>.

You can see how the **unique id** changes every time the <mark style="color:orange;">`datasource`</mark> 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:

![In-Memory DataSource decorators](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F4MoO9WAp5W3RAfuzPtt2%2Fin-memory-datasource-decorators.png?alt=media\&token=40803e77-f581-4565-966c-127f8f342757)

{% hint style="success" %}
The <mark style="color:green;">`DikeGridDataSource`</mark> instance is always the first instance in the chain.
{% endhint %}

As you can see, the DikeGrid creates the following instances:

| Decorator class                                                | Description                                                                                                                                                                                                                                                              |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <mark style="color:green;">`DikeRefreshingDataSource`</mark>   | This decorator always wraps a <mark style="color:green;">`DikeGridDataSource`</mark> instance. It pushes the rows when the user has **updated** them. The DikeGrid creates this decorator if you have allowed the edition operation.                                     |
| <mark style="color:green;">`DikeEditionStateDataSource`</mark> | It splits the set of rows depending on their status, <mark style="color:red;">`Modified`</mark>, <mark style="color:red;">`Deleted`</mark>, or <mark style="color:red;">`Editing`</mark>. The DikeGrid creates this decorator if you have allowed the edition operation. |
| <mark style="color:green;">`DikeFilteringDataSource`</mark>    | This decorator manages all the filtering operations. Even the filters applied to the rows in edition mode.                                                                                                                                                               |
| <mark style="color:green;">`DikeSortingDataSource`</mark>      | This decorator manages all the sorting operations.                                                                                                                                                                                                                       |

We based on the [Decorator](https://refactoring.guru/design-patterns/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.

{% hint style="info" %}
The DikeGrid will set the <mark style="color:green;">`DikePagingDataSource`</mark> decorator at the end of the chain of operations or after the created tree due to the row grouping operation.
{% endhint %}

### 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 <mark style="color:blue;">`abstract`</mark> class [<mark style="color:green;">`DikeDecoratorDataSource`</mark>](https://docs.dikesoft.com/reference/classes/datasource#dikedecoratordatasource-less-than-t-greater-than).

Let us define a custom decorator:

{% code title="custom-datasource-decorator.ts" %}

```typescript
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);
  }
}

```

{% endcode %}

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 [<mark style="color:green;">`DataSourceDecoratorFn`</mark>](https://docs.dikesoft.com/reference/type-aliases/datasource#datasourcedecoratorfn-less-than-t-greater-than). Then, you will assign it to the input property named <mark style="color:orange;">`decoratorFn`</mark>.

{% hint style="warning" %}
Be aware that you can assign the <mark style="color:orange;">`decoratorFn`</mark> property only at the initialization phase.
{% endhint %}

Let us assign the <mark style="color:orange;">`decoratorFn`</mark> property.

{% code title="in-memory-data-source.component.ts" %}

```typescript
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;
  };
}
```

{% endcode %}

As you can see, you have access to the property named <mark style="color:orange;">`dataSourceFactory`</mark> under the DikeGrid instance.

{% hint style="success" %}
The <mark style="color:orange;">`dataSourceFactory`</mark> property is of type [<mark style="color:green;">`DikeGridFactoryDataSource`</mark>](https://docs.dikesoft.com/reference/dkgrid-api/dkgridfactorydatasource). It is a **read-only** property used to create **filtering** and **sorting** decorators.
{% endhint %}

## 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](https://refactoring.guru/design-patterns/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

{% tabs %}
{% tab title="in-memory-data-source.component.html" %}

```markup
<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>
  
```

{% endtab %}

{% tab title="in-memory-data-source.component.ts" %}

```typescript
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
})
export class InMemoryDataSourceComponent implements OnInit, 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 = new CustomDecoratorDataSource(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: new Date(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: new Date(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: new Date(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: new Date(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: new Date(2016, 10, 19),
        country: 'United States of America'
    }] as Partial<Employee>[];
  }

  onDikeGridDataSource(): void {
    // Create a DikeGridDataSource object:
    const gridDataSource = new DikeGridDataSource<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();
    });
  }
}

```

{% endtab %}

{% tab title="custom-datasource-decorator.ts" %}

```typescript
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';

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);
  }
}

```

{% endtab %}
{% endtabs %}
