# Row Selection

## Live example

{% hint style="success" %}
[Row Selection](https://demos.dikesoft.com/dk-grid/row/selection) live example.
{% endhint %}

## Allowing the selection operation

To allow the user to **select/deselect** the DikeGrid rows, you must provide an input property named <mark style="color:orange;">`allowSelection`</mark>. This property is at the grid scope.

{% code title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    [allowSelection]="gridProperties.allowSelection">
</dike-grid>
```

{% endcode %}

{% hint style="info" %}
You can change the value of the <mark style="color:orange;">`allowSelection`</mark> property at **runtime**.
{% endhint %}

Open the [Floating Configuration Panel](https://docs.dikesoft.com/overview#floating-configuration-panel) for the live example and click on the **Allow Selection** checkbox.

![Floating Configuration Panel - Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FR0ZZq1Pf1grFV8IN86tZ%2Fselection-panel-conf.png?alt=media\&token=1756c6f8-11e7-499d-a420-228c9c2e57cb)

Once you have enabled selection, you will see a **checkbox** column displayed for every row. To **select/deselect** a row, click on the related checkbox.

![Enabling Row Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FvwEtfa5Q6uDtn8HQlO6S%2Frow-selection-enabled.png?alt=media\&token=b60b948f-ae0d-4e8a-8d75-7e8bd58e54bc)

## Selectable rows

By default, when you enable selection for the DikeGrid, all rows are susceptible to being selected.

If you want the user can select only **some rows**, you can provide a **function** specifying the conditions the rows must meet. You can set this function by providing a property named <mark style="color:orange;">`selectableRows`</mark>.

Let us define this function to allow the user to select rows whose age value is not 27.

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    [selectableRows]="selectableRowsDefinition">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
selectableRowsDefinition(entry: Employee): boolean {
    // Allow selection only for those rows whose age value is different of 27:
    return entry.age !== 27;
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
The <mark style="color:orange;">`selectableRows`</mark> property is of type [<mark style="color:green;">`SelectableFn`</mark>](https://docs.dikesoft.com/reference/dkgrid-api/dkgridselection#properties), and you can also provide this function using the **selection API**.
{% endhint %}

{% hint style="info" %}
You can change this function definition at **runtime**. Therefore, the DikeGrid instance will evaluate the currently selected rows, deselecting those rows that do not meet the new criteria.
{% endhint %}

As you can see, rows that do not meet the defined criteria display their checkbox **disabled**.

![Defining the selectableRows function](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FBVgDc5ps7XOzKv1Z2yUp%2Frow-selection-selectableFn.png?alt=media\&token=59368c0b-51ce-4570-af01-acc94dbafa72)

## Header Checkbox Selection

When you provide an **in-memory** data set, the DikeGrid instance always shows a checkbox in its header.

On the other hand, the DikeGrid instance will not show a checkbox in its header when you provide a **custom Datasource** because the DikeGrid does not know the total length of the provided data set.

{% hint style="success" %}
See the [Datasource](https://docs.dikesoft.com/fundamentals/datasource) section for further details about an **in-memory** data set and a **custom Datasource**.
{% endhint %}

By default, the checkbox in the header will select all rows even though the data set is filtered. However, if you want to select only the filtered rows, you can use the **selection API**.

## Group Selection

When you group rows by a column, every group will show its checkbox.

1. The selectable rows under the group will be selected when you click on the checkbox in the group. If the group has **non-selectable** rows, its state will be **indeterminate**.
2. When you click in any of the rows under the group, the checkbox in the group will update its status to indeterminate and become checked until you select all rows.

{% hint style="info" %}
The checkbox in the header will update its state as well.
{% endhint %}

We have enabled **row grouping** in the live demo to see this feature.

{% code title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    allowRowGrouping
    allowRowFiltering
    allowColumnDragging>
    
    <dike-grid-column
        fieldName="gender"
        headerText="Gender"
        dataType="Binary"
        width="130"
        draggable
        groupable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="age"
        headerText="Age"
        dataType="Numeric"
        contentAlign="center"
        width="100"
        draggable
        groupable>
    </dike-grid-column>
</dike-grid>
```

{% endcode %}

As you can see, apart from allowing **row grouping**, we have enabled **column dragging**. Then, we set **Gender** and **Age** columns being **groupable**.&#x20;

You can move the **Age** column to the group panel with the previous configuration. Thus, you can select any group or a row under any group.

![Group Selection](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FInW4AtuMRKPVL5V3qgiY%2Frow-selection-groups.png?alt=media\&token=bd1a87ed-249f-4547-a049-35d8415f7d09)

## DikeGrid Selection API

You can also use the **selection API** to select rows.

Before using the **selection API**, you must retrieve the related DikeGrid instance by querying the component's view.

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
@Component({
  selector: 'row-selection',
  templateUrl: './row-selection.component.html',
  styleUrls: ['./row-selection.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class RowSelectionComponent implements OnInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;
  // ...
}
```

{% endtab %}
{% endtabs %}

The **selection API** has the following methods:

| Method                 | Description                                                                                                                                                                                                            |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `selectOne()`          | <ol><li>It marks the given row as selected. </li><li>The given row must pass the selectable function. </li><li>This method emits the <code>selectionChange</code> and <code>selectedRowChange</code> events.</li></ol> |
| `select()`             | <ol><li>It marks the given rows as selected. </li><li>It emits the <code>selectionChange</code> event.</li></ol>                                                                                                       |
| `deselectOne()`        | <ol><li>It turns off the selected flag for the given row.</li><li>This method emits the <code>selectionChange</code> and <code>deselectedRowChange</code> events.</li></ol>                                            |
| `deselect()`           | <ol><li>It turns off the selected flag for the given rows.</li><li>This method emits the <code>selectionChange</code> event.</li></ol>                                                                                 |
| `getSelectedRows()`    | This method returns an array of all selected rows.                                                                                                                                                                     |
| `getSelectedEntries()` | It returns all selected entries.                                                                                                                                                                                       |
| `selectAll()`          | This method will set all rows coming from the data source as selected.                                                                                                                                                 |
| `deselectAll()`        | It turns off the selection flag of all selected rows.                                                                                                                                                                  |
| `resetSelection()`     | This method deselects all selected rows and cleanses some internal variables. The DikeGrid instance will invoke this method every time you provide a new data set.                                                     |

{% hint style="success" %}
For further details, see the [<mark style="color:green;">`DikeGridSelection`</mark>](https://docs.dikesoft.com/reference/dkgrid-api/dkgridselection#methods) definition.
{% endhint %}

{% hint style="info" %}
To use any previous methods, you must have allowed selection by providing the input property named <mark style="color:orange;">`allowSelection`</mark>.
{% endhint %}

Remember, when you provide a data source, the DikeGrid instance **wraps** every entry into an object of type <mark style="color:green;">`DikeGridRowEntry`</mark>. See the class hierarchy for the DikeGrid rows.

To see the **selection API** in action, we have created the following UI:

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onSelectOne()">selectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onSelect()">select / filtered
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onDeselectOne()">deselectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onDeselect()">deselect / filtered
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetSelectedRows()">getSelectedRows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetSelectedEntries()">getSelectedEntries
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onSelectAll()">selectAll
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDeselectAll()">deselectAll
        </button>
    </div>
</div>

<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid">
</dike-grid>

```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
onSelectOne(): void {
  const filteredRows = this.dikeGrid.filter.getFilteredRows();

  if (filteredRows.length > 0) {
    this.dikeGrid.selection.selectOne(filteredRows[0]);
  }
}

onSelect(): void {
  const filteredRows = this.dikeGrid.filter.getFilteredRows();

  if (filteredRows.length > 0) {
    this.dikeGrid.selection.select(filteredRows);
  }
}

onDeselectOne(): void {
  const selectedFromFiltered = this.dikeGrid.filter.getFilteredRows().filter(row => row.selected);

  if (selectedFromFiltered.length > 0) {
    this.dikeGrid.selection.deselectOne(selectedFromFiltered[0]);
  }
}

onDeselect(): void {
  const filteredRows = this.dikeGrid.filter.getFilteredRows();

  if (filteredRows.length > 0) {
    this.dikeGrid.selection.deselect(filteredRows);
  }
}

onGetSelectedRows(): void {
  console.log('Selected rows: ', this.dikeGrid.selection.getSelectedRows());
}

onGetSelectedEntries(): void {
  console.log('Selected entries: ', this.dikeGrid.selection.getSelectedEntries());
}

onSelectAll(): void {
  this.dikeGrid.selection.selectAll();
}

onDeselectAll(): void {
  this.dikeGrid.selection.deselectAll();
}

```

{% endtab %}
{% endtabs %}

The previous HTML code generates the following output:

![Row Selection using the API](https://3888584995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpDxfe6pgRLqBLMQgZ0kG%2Fuploads%2FkWFkpQqW3ciWBUtTw4PC%2Frow-selection-ui-for-api.png?alt=media\&token=9e8f69e4-4317-4abb-a673-e9af34be626a)

For every button, we have attached the following actions:

1. Buttons **selectOne / filtered**, **select / filtered**, **deselectOne / filtered** and **deselect / filtered** work over the filtered set. These buttons are enabled if you have filtered the original set.
2. To allow filtering, we enabled the DikeGrid **Row Filter**.
3. The **selectAll** and **deselectAll** buttons are self-explanatory.
4. We printed out the **selected rows** and **selected entries** in the **dev console**.

Please, open the **live demo** and see the previous buttons in action.

To evaluate if the DikeGrid instance has a filter applied, we listen to the `filterChange` and `columnsChange` events.

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    (columnsChange)="onColumnsChange($event)"
    (filterChange)="onFilterChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
onFilterChange(filterable: DikeFilterable<Employee>): void {
  // We ignore the argument filterable because we are only interested in the action.
  this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
}

onColumnsChange(columns: DikeColumnDef[]): void {
  // We ignore the argument columns because we are only interested in the action.
  this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
}
```

{% endtab %}
{% endtabs %}

1. The `filterChange` event fires every time the user types something in the DikeGrid **Row Filter**.
2. We listen to the `columnsChange` event because every time the user groups the rows by a column, the user must move that column to the group panel, provoking the DikeGrid instance to raise the `columnsChange` event.

{% hint style="warning" %}
Remember that the DikeGrid ignores filters for the columns in the group panel.
{% endhint %}

## Selection events

Every time we **select** or **deselect** one or more rows, the DikeGrid instance emits the related event.

The following are the selection events:

| Event               | Description                                                |
| ------------------- | ---------------------------------------------------------- |
| `selectedRowChange` | It emits when the user selects one row.                    |
| `deselectRowChange` | It emits when the user deselects one row.                  |
| `selectionChange`   | It emits when the user selects/deselects one or more rows. |

We listen to these three events:

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    (selectedRowChange)="onSelectedRowChange($event)"
    (deselectedRowChange)="onDeselectedRowChange($event)"
    (selectionChange)="onSelectionChange($event)">
</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

```typescript
onSelectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
  console.log('Select one row change: ', row);
}

onDeselectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
  console.log('Deselect one row change: ', row);
}

onSelectionChange(rows: DikeGridDataRowEntry<Employee>[]): void {
  console.log('Selection change: ', rows);
}
```

{% endtab %}
{% endtabs %}

{% hint style="success" %}
Please, open the **dev console** to see the output of these events.
{% endhint %}

{% hint style="info" %}
You can also listen to these events from the [<mark style="color:green;">`DikeGridSelection`</mark>](https://docs.dikesoft.com/reference/dkgrid-api/dkgridselection#events) instance.
{% endhint %}

## Summary

To perform the **selection** operation, you must allow it by providing an input property named <mark style="color:orange;">`allowSelection`</mark>. By default, the user can select all rows, but you can enable only some rows for selection by providing a **custom function** where every row must meet the established criteria. You can **select** or **deselect** rows through the **UI** or the **selection API**.

### Complete code for this section

{% tabs %}
{% tab title="row-selection.component.html" %}

```markup
<div class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onSelectOne()">selectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onSelect()">select / filtered
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onDeselectOne()">deselectOne / filtered
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!isFiltered"
            (click)="onDeselect()">deselect / filtered
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetSelectedRows()">getSelectedRows
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onGetSelectedEntries()">getSelectedEntries
        </button>
    </div>

    <div class="flex-none w-56 flex flex-col m-2 items-center">
        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onSelectAll()">selectAll
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            (click)="onDeselectAll()">deselectAll
        </button>
    </div>
</div>

<dike-grid id="grid-row-selection" height="700px" #grid="dkgGrid"
    [displayRowId]="gridProperties.displayRowId"
    [gridElevation]="gridProperties.matElevation"
    [gridElevationValue]="gridProperties.elevationValue"
    [striped]="gridProperties.stripeRows"
    [verticalRowLines]="gridProperties.verticalRowLines"

    (columnsChange)="onColumnsChange($event)"
    (filterChange)="onFilterChange($event)"
    (selectedRowChange)="onSelectedRowChange($event)"
    (deselectedRowChange)="onDeselectedRowChange($event)"
    (selectionChange)="onSelectionChange($event)"
    [selectableRows]="selectableRowsDefinition"

    allowRowGrouping
    allowRowFiltering
    allowColumnDragging
    [allowSelection]="gridProperties.allowSelection"
    [datasource]="dkgDataSource">

    <dike-grid-column
        fieldName="employeeId"
        headerText="Employee Id"
        dataType="Text"
        width="350">
    </dike-grid-column>

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

    <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="130"
        draggable
        groupable>
    </dike-grid-column>

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

    <dike-grid-column
        fieldName="email"
        headerText="Email"
        dataType="Text"
        width="300">
    </dike-grid-column>

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

</dike-grid>
```

{% endtab %}

{% tab title="row-selection.component.ts" %}

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

import { Subscription } from 'rxjs';

import { DikeGridComponent, DikeGridDataRowEntry, 
  DikeGridDataSourceInput, DikeFilterable, DikeColumnDef 
} 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: 'row-selection',
  templateUrl: './row-selection.component.html',
  styleUrls: ['./row-selection.component.scss'],

  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class RowSelectionComponent implements OnInit, OnDestroy {
  // Retrieve the DikeGridComponent<T> instance from the view:
  @ViewChild('grid') dikeGrid: DikeGridComponent<Employee>;

  dkgDataSource: DikeGridDataSourceInput<Employee>;
  gridProperties: DikeGridProperties;

  isFiltered: boolean;

  private changeGridPropertiesSubscription: Subscription = Subscription.EMPTY;

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

    this.isFiltered = false;
  }

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

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

  selectableRowsDefinition(entry: Employee): boolean {
    // Allow selection only for those rows whose age value is different of 27:
    return entry.age !== 27;
  }

  onSelectOne(): void {
    const filteredRows = this.dikeGrid.filter.getFilteredRows();

    if (filteredRows.length > 0) {
      this.dikeGrid.selection.selectOne(filteredRows[0]);
    }
  }

  onSelect(): void {
    const filteredRows = this.dikeGrid.filter.getFilteredRows();

    if (filteredRows.length > 0) {
      this.dikeGrid.selection.select(filteredRows);
    }
  }

  onDeselectOne(): void {
    const selectedFromFiltered = this.dikeGrid.filter.getFilteredRows().filter(row => row.selected);

    if (selectedFromFiltered.length > 0) {
      this.dikeGrid.selection.deselectOne(selectedFromFiltered[0]);
    }
  }

  onDeselect(): void {
    const filteredRows = this.dikeGrid.filter.getFilteredRows();

    if (filteredRows.length > 0) {
      this.dikeGrid.selection.deselect(filteredRows);
    }
  }

  onGetSelectedRows(): void {
    console.log('Selected rows: ', this.dikeGrid.selection.getSelectedRows());
  }

  onGetSelectedEntries(): void {
    console.log('Selected entries: ', this.dikeGrid.selection.getSelectedEntries());
  }

  onSelectAll(): void {
    this.dikeGrid.selection.selectAll();
  }

  onDeselectAll(): void {
    this.dikeGrid.selection.deselectAll();
  }

  onFilterChange(filterable: DikeFilterable<Employee>): void {
    // We ignore the argument filterable because we are only interested in the action.
    this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
  }

  onColumnsChange(columns: DikeColumnDef[]): void {
    // We ignore the argument columns because we are only interested in the action.
    this.isFiltered = !!this.dikeGrid ? this.dikeGrid.filter.isFilterApplied() : false;
  }

  onSelectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
    console.log('Select one row change: ', row);
  }

  onDeselectedRowChange(row: DikeGridDataRowEntry<Employee>): void {
    console.log('Deselect one row change: ', row);
  }

  onSelectionChange(rows: DikeGridDataRowEntry<Employee>[]): void {
    console.log('Selection change: ', rows);
  }

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

{% endtab %}
{% endtabs %}
