# 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](/files/JYknyYHrgAn0DksOFHGZ)

{% 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](/files/OY61OQ07UXpdCrrDfFZg)

{% 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>](/reference/dkgrid-api/dkgridcolumndef.md) 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](/files/QH7tk97e1NMFKAHartbn)

## 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.dikesoft.com/columns/column-grouping.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
