# Column Grouping

## Live example

{% hint style="success" %}
[Column Grouping](https://demos.dikesoft.com/dk-grid/column/grouping) live example.
{% endhint %}

## Groups Definition

To create a column group, you nest a column definition under another column definition. You can nest column definitions as many as you need.

Let us define the following column groups:

1. Firstly, We have two main groups: **Personal Info** and **Employee Info** column groups.
2. Under the **Personal Info** group, we have the **Complete Name** column group, the **Gender** and **Age** columns.
3. Under the **Complete Name** column group, we have **Name** and **Surname** columns.
4. Lastly, under the **Employee Info**, we have the **Employee Id**, **Email**, and **Hire Date** columns.

In code, we have:

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid id="grid-col-grouping" height="600px"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="1">

        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name">

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

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

        </dike-grid-column>

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110">
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85">
        </dike-grid-column>

    </dike-grid-column>

</dike-grid>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';

import { Subscription } from 'rxjs';

import { DikeDateColumnDef, DikeGridColumnDef, 
  DikeGridDataSourceInput, DikeTextColumnDef, DikeGroupColumnDef 
} 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 { SampleData } from 'app/services/sample-data.service';
import { DikeGridConfig } from 'app/services/dike-grid.config.service';

@Component({
  selector: 'column-grouping',
  templateUrl: './column-grouping.component.html',
  styleUrls: ['./column-grouping.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnGroupingComponent implements OnInit, OnDestroy {

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

  constructor(
    private cdr: ChangeDetectorRef,
    private gridConfig: DikeGridConfig,
    private sampleData: SampleData) { }

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 250;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.width = 120;

    // Define the Employee Id column:
    const employeeIdColumn = new DikeTextColumnDef<Employee>('employeeId', 'Employee Id');
    employeeIdColumn.width = 350;

    // Lastly, define the Employee Info column group:
    const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
    employeeInfo.order = 2;

    // Add columns to the column group:
    employeeInfo.children = [ employeeIdColumn, emailColumn, hireDateColumn ];

    // Then, add the colum group to the DikeGridComponent instance:
    columnDef.addColumns(employeeInfo);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

  ngOnDestroy(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
  }

  private setChangeGridPropertiesSubscription(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
    this.changeGridPropertiesSubscription = this.gridConfig.configChange.subscribe((props: DikeGridProperties) => {
      this.gridProperties = props;
      this.cdr.markForCheck();
    });
  }
}

```

{% endtab %}
{% endtabs %}

As you can see in the previous definition, we create the column groups in the **HTML definition** and **TypeScript code**, as we define any column.

You must not provide the `dataType` property because column groups can hold any number of columns, even column groups.

![Column Grouping definition](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FsasD7AtNCuvpF5zOFCjR%2Fcolumn-grouping-definition.png?alt=media\&token=590ea8a2-a30e-4897-a8f2-4d18916cb8a2)

{% hint style="danger" %}
Be aware when you create a column group in the TypeScritpt code, the children property is null.
{% endhint %}

## Group Status

Any group could be ***open*** or ***closed***. The property `displayStatus` stores the group status.

If you want a group could open or close, its child columns must tell the grid which columns are visible in closed status.

Let us define another column under the **Employee Info** group. This column will be visible when the **Employee Info** column group is closed, displaying the **Total Sales**.

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid>...</dike-grid>
<ng-template #totalSales let-value="fieldValue">
    <p>{{ value | currency }}</p>
</ng-template>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

```typescript
onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {    
    // Define the Total Sales column:
    const totalSalesColumn = new DikeNumericColumnDef<Employee>('totalSales', 'Total Sales');
    totalSalesColumn.width = 150;
    totalSalesColumn.displayTemplate = from(Promise.resolve().then(() => this.totalSalesTpl));
    totalSalesColumn.displayOn = 'closed';
    
    // Lastly, define the Employee Info group:
    const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
    employeeInfo.order = 2;
    
    // Add columns to the column group:
    employeeInfo.children = [ employeeIdColumn, emailColumn, hireDateColumn, totalSalesColumn ];
    
    // Then, add the colum group to the DikeGridComponent instance:
    columnDef.addColumns(employeeInfo);
}
```

{% endtab %}
{% endtabs %}

In the previous code, we have defined the following:

1. Firstly, we determined the template for displaying the total sales field.
2. Then, we create the actual column definition for **Total Sales**. The `displayOn` property is equal to **closed**, meaning that the user will see this value when the **Employee Info** group is closed.

![Column Group Status](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2F3K0N4MloG6F2A6XfifSP%2Fcolumn-grouping-open-close.gif?alt=media\&token=711be263-e859-4ea5-8501-9eed08dfe872)

{% hint style="info" %}
The groups that can open or close will have a **right** or **left** arrow, indicating that they can change their status.
{% endhint %}

{% hint style="info" %}
If you do not specify the `displayOn` property, the default value will be ***open***.
{% endhint %}

### Initial Group Status

By default, the initial status of a group is ***open***. However, you can change this value by providing the ***closed*** value to the `displayStatus` property.

Let us change the initial status for the **Employee Info** group to a closed value.

{% code title="column-grouping.component.ts" %}

```typescript
// Lastly, define the Employee Info group:
const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
employeeInfo.order = 2;
employeeInfo.displayStatus = 'closed';
```

{% endcode %}

When you launch the HTML containing the DikeGrid definition, the **Employee Info** group will appear closed.

{% hint style="warning" %}
If you set a group as ***closed*** and none of the child columns has the `displayOn` property to a ***closed*** value, the group status will be ***open***.

If you set a group as ***open*** and all the child columns have the `displayOn` property to a ***closed*** value, the group status will be ***open***.
{% endhint %}

### Group Status Event

When a column group changes its status, the DikeGrid instance emits an event. The name of the event is `columnGroupStatusChange`.

Let us listen to this group status event from the DikeGrid instance directly.

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid id="grid-col-grouping" height="600px"
    (columnGroupStatusChange)="onColumnGroupStatusChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

```typescript
onColumnGroupStatusChange(columnGroup: DikeGroupColumnDef): void {
    console.log('onColumnGroupStatusChange: ', columnGroup);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
You can listen to this event from the [<mark style="color:green;">`DikeGridColumnDef`</mark>](https://docs.dikesoft.com/reference/dkgrid-api/dkgridcolumndef) service through the `columnGroupStatusChange` property.
{% endhint %}

Open the **Personal Info** group and see the event information in the **dev console**.

## Column Chooser Menu

When you navigate to the **Column Chooser Menu** and open the **Employee Info** group, you will see a circle with an **accent** color next to the **Total Sales** column.

The circle next to the **Total Sales** column indicates that the column is visible when the parent group is ***closed***.

![DisplayOn indicator](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2Fgl8iMu7aa7NM9sMsJfvq%2Fcolumn-grouping-circle-indicator.png?alt=media\&token=4a824eee-142d-4511-a091-fc3126c45d59)

## Summary

In this section, we explored column groups creation and its status. Then, we listen to the event sent when this status has changed and spot a column displayed in a closed position.

### Complete code for this section

{% tabs %}
{% tab title="column-grouping.component.html" %}

```markup
<dike-grid id="grid-col-grouping" height="600px"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (gridColumnDefInstance)="onColumnDefInstance($event)"
    (columnGroupStatusChange)="onColumnGroupStatusChange($event)"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="1">

        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name">

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

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

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110">
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85">
        </dike-grid-column>
    </dike-grid-column>
</dike-grid>

<ng-template #totalSales let-value="fieldValue">
    <p>{{ value | currency }}</p>
</ng-template>
```

{% endtab %}

{% tab title="column-grouping.component.ts" %}

```typescript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';

import { from, Subscription } from 'rxjs';

import { DikeDateColumnDef, DikeGridColumnDef, 
  DikeGridDataSourceInput, DikeTextColumnDef, DikeGroupColumnDef, DikeNumericColumnDef 
} 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 { SampleData } from 'app/services/sample-data.service';
import { DikeGridConfig } from 'app/services/dike-grid.config.service';

@Component({
  selector: 'column-grouping',
  templateUrl: './column-grouping.component.html',
  styleUrls: ['./column-grouping.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ColumnGroupingComponent implements OnInit, OnDestroy {
  // Retrieve the corresponding templates:
  @ViewChild('totalSales', { read: TemplateRef }) totalSalesTpl: TemplateRef<any>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

  constructor(
    private cdr: ChangeDetectorRef,
    private gridConfig: DikeGridConfig,
    private sampleData: SampleData) { }

  onColumnDefInstance(columnDef: DikeGridColumnDef<Employee>): void {
    // Define the Email column:
    const emailColumn = new DikeTextColumnDef<Employee>('email', 'Email');
    emailColumn.width = 250;

    // Define the Hire Date column:
    const hireDateColumn = new DikeDateColumnDef<Employee>('hireDate', 'Hire Date');
    hireDateColumn.width = 120;

    // Define the Employee Id column:
    const employeeIdColumn = new DikeTextColumnDef<Employee>('employeeId', 'Employee Id');
    employeeIdColumn.width = 350;

    // Define the Total Sales column:
    const totalSalesColumn = new DikeNumericColumnDef<Employee>('totalSales', 'Total Sales');
    totalSalesColumn.width = 150;
    totalSalesColumn.displayTemplate = from(Promise.resolve().then(() => this.totalSalesTpl));
    totalSalesColumn.displayOn = 'closed';

    // Lastly, define the Employee Info group:
    const employeeInfo = new DikeGroupColumnDef('employeeInfoGroup', 'Employee Info');
    employeeInfo.order = 2;
    employeeInfo.displayStatus = 'closed';

    // Add columns to the column group:
    employeeInfo.children = [ employeeIdColumn, emailColumn, hireDateColumn, totalSalesColumn ];

    // Then, add the colum group to the DikeGridComponent instance:
    columnDef.addColumns(employeeInfo);
  }

  ngOnInit(): void {
    this.dkgDataSource = this.sampleData.getEmployees(1000);
    // Listening to any config property change:
    this.setChangeGridPropertiesSubscription();
  }

  ngOnDestroy(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
  }

  onColumnGroupStatusChange(columnGroup: DikeGroupColumnDef): void {
    console.log('onColumnGroupStatusChange: ', columnGroup);
  }
  
  private setChangeGridPropertiesSubscription(): void {
    this.changeGridPropertiesSubscription.unsubscribe();
    this.changeGridPropertiesSubscription = this.gridConfig.configChange.subscribe((props: DikeGridProperties) => {
      this.gridProperties = props;
      this.cdr.markForCheck();
    });
  }
}

```

{% endtab %}
{% endtabs %}
