Column Moving

This section describes how to make a column draggable and which type of movements you can do.

Live example

Column Moving live example.

Allowing column dragging

To allow the user moves a column, you must set a flag at the grid scope by providing an input DikeGrid property named allowColumnDragging. Its default value is false.

column-moving.component.html
<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    [allowColumnDragging]="gridProperties.allowColumnDragging">
</dike-grid>

You can change the value of the allowColumnDragging property at runtime.

You can open the Floating Configuration Panel of the live demo and change the value of the property allowColumnDragging.

Draggable Columns

You can make a column movable by providing a property named draggable. Its default value is false.

Be aware that you can not change the draggable property at runtime. Use the locked property instead if you want the user not to move a column at runtime. For more details, see the Column Pinning section.

Let us set the following column configuration:

column-moving.component.html
<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    allowRowGrouping
    allowColumnDragging>

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

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

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        minWidth="50"
        maxWidth="300"
        order="1"
        resizable
        draggable
        groupable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="0"
        resizable
        draggable>
        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name"
            draggable>

            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>

            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
        </dike-grid-column>

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110"
            minWidth="50"
            maxWidth="200"
            resizable
            draggable
            groupable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85"
            minWidth="50"
            maxWidth="120"
            resizable
            draggable
            groupable>
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        order="1"
        draggable
        groupable
        panel="rightPanel">
    </dike-grid-column>
</dike-grid>

With this configuration, we have:

  1. All columns and column groups are movable except the Name column in the root group and the Name and Surname columns under the Complete Name group.

  2. We have enabled the row grouping to move columns to the group panel. Columns Surname and Hire Date from the root group are groupable. Gender and Age columns under the Personal Info group are groupable as well.

  3. All columns are in the center panel except the Hire Date column in the right panel.

You can move only data columns to the group panel, not column groups.

When you move a column group to the group panel using the API, the movement inserts only the descendant data columns into the group panel.

Column movements

Once we have established some columns as draggable, be aware of the valid movements a column could take.

For the description of the following movements, it will be helpful to grasp the Gutters structure of the DikeGrid.

Interchange Columns

This movement will swap two columns. The source column will take the destination column's place and vice versa. For this movement, the following rules apply:

  1. The columns must belong to the same group of columns.

  2. You can interchange columns that belong to the Content panels.

If you try to swap a broken group, you will join the group in the destination panel.

Move Column Before

You can move a column before another column.

Let us say that you can move a given column at the left side of the target column. The actual movement will insert the given column at the left-gutter of the target column. Therefore, consider the following rules:

  1. Columns must belong to the same group of columns.

  2. The target column must belong to the right panel because columns have their gutter at their left.

  3. If the target column lives in the left, center panel, or group panel, it must be the first column because they have both gutters.

Move Column After

You can move one column after another column.

Same as before, let us say that you can move a given column at the right side of the target column. The actual movement will insert the given column at the right-gutter of the target column.

The rules are:

  1. Columns must belong to the same group of columns.

  2. The target column must belong to the group, left or center panel because columns have their gutter at their right.

  3. If the target column lives in the right panel, it must be the last column because it has both gutters.

Column to panel

This movement is possible when you drag a column to the left or right grid border or use the Pinning submenu of the Column Context Menu.

Drag a column to the grid's borders

Drag a column to the left or right grid border, as you can see in the following gif.

Using the Column Context Menu

Now, click on any Column Context Menu and select a panel to move the column.

It is possible to move all panel columns to another panel using the column API.

Breaking a column group

You can take a column that belongs to a column group and place it in another panel. This move grabs the column with its parent column and its parent, and so forth until the root.

The movement's outcome is a broken group. For example, we moved the Gender column to the left panel. See the final state of the Personal Info group in the following screenshot.

Unbroken column groups

If you do not want the user breaks a group, make its child columns non-draggable, as we did for Name and Surname columns under the Complete Name column group.

column-moving.component.html
<dike-grid-column
        fieldName="completeNameGroup"
        headerText="Complete Name"
        draggable>
        <dike-grid-column
            fieldName="firstName"
            headerText="Name"
            dataType="Text"
            width="150"
            minWidth="50"
            maxWidth="300"
            resizable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="lastName"
            headerText="Surname"
            dataType="Text"
            width="150"
            minWidth="50"
            maxWidth="300"
            resizable>
        </dike-grid-column>
</dike-grid-column>

You can not move the Name column or the Surname column. You can only move those columns by dragging the Complete Name group.

When you drag a column, the DikeGrid instance highlights the proper columns or gutters to drop the column you are moving.

Move columns using the API

You can achieve the previous movements using the column API.

Remember, to use the column API, you must grab the instance of the DikeGridColumnDefservice.

The following table shows the desired movement and its related method in the column API.

For further details, see the DikeGridColumnDefdefinition.

Even though you do not set the flag allowColumnDragging, you can move a column using the API, but the column must be set as draggable and not locked.

To see the use of the API, we have created the following UI:

column-moving.component.html
<form [formGroup]="moveColumnsForm"
    class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Source</mat-label>
        <mat-select formControlName="source" required
            (selectionChange)="onSourceSelectionChange($event)">
            <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of gridPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Destination</mat-label>
        <mat-select formControlName="destination" required
            (selectionChange)="onDestinationSelectionChange($event)">
            <mat-option *ngFor="let column of destinationColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of destinationPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-radio-group class="flex-none w-40 m-2 flex flex-col"
        #radioMethods="matRadioGroup" formControlName="method" required>
        <mat-radio-button class="my-2" value="swap-columns">swap-columns</mat-radio-button>
        <mat-radio-button class="my-2" value="column-after">column-after</mat-radio-button>
        <mat-radio-button class="my-2" value="column-before">column-before</mat-radio-button>
    </mat-radio-group>

    <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]="radioMethods.disabled || !moveColumnsForm.valid"
            (click)="onMoveColumns()">Move column(s)
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!radioMethods.disabled"
            (click)="onMoveColumnsToPanel()">Move column(s) to Panel
        </button>
    </div>
</form>

We added the form selector above the dike-grid selector, generating the following output:

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

  1. You can choose the source column or the source panel from the first select control.

  2. Depending on your selected option, the second dropdown control will have a proper destination place. For instance, if you choose a column, the destination control will contain all columns of the same group and panels the column does not live.

  3. The radio-button group has methods that involve column between column movements.

  4. When you select panels, the corresponding button is enabled.

It is essential to notice that we are listening to any change in the DikeGrid's columns, attaching a subscription to the columnsChange Observable from the DikeGridColumnDef.

column-moving.component.ts
private setChangeGridColumnsSubscription(): void {
  this.changeGridColumnsSubscription.unsubscribe();
  this.changeGridColumnsSubscription = this.dikeGrid.columnDef.columnsChange.pipe(
    map((columns: DikeColumnDef[]) => this.flatColumns(columns)),
    observeOn(asapScheduler)

  ).subscribe((columns) => {
    this.destinationColumns = null;
    this.destinationPanels = null;
    this.moveColumnsForm.patchValue({ source: null, destination: null, method: '' });
    this.moveColumnsForm.get('method').enable();
    this.gridColumns = columns;
  });
}

Every time we receive a new set of columns, we flat them into a single array, invoking the method flatColumns(). So indeed, we are flattening the column groups, traversing them recursively.

column-moving.component.ts
private flatColumns(columns: DikeColumnDef[]): DikeColumnDef[] {
  return columns.reduce((accum, column) => {
    if (isDikeGroupColumnDef(column) && !!column.children && column.children.length > 0) {
      return [ ...accum, column, ...this.flatColumns(column.children) ];
    }

    return [ ...accum, column ];
  }, [ ]);
}

If you see the content of the source dropdown control, you see the columns Hire Date, Employee Id, Personal Info group, Complete Name group, then Name and Surname columns, Gender and Age columns, etc.

The non-draggable columns are disabled, such as Name and Surname, under the Complete Name group.

Column Moving Events

Every previous movement emits an event of type DikeColumnMoveEvent. We listen to the columnMove event at the grid scope.

<dike-grid id="grid-col-moving" height="650px" #grid="dkgGrid"
    (columnMove)="onColumnMove($event)">
</dike-grid>

You can also listen to the columnMove event from the DikeGridColumnDef<T> instance.

See the definition of the DikeColumnMoveEvent interface:

interface DikeColumnMoveEvent {
  type: ColumnMovement;
  movedColumns: DikeColumnDef[];
  beforeColumn?: DikeColumnDef;
  afterColumn?: DikeColumnDef;
  destinationPanel?: DikeGridPanel;
}

And the ColumnMovement definition is:

type ColumnMovement = 'swap-columns' | 'join-column-groups' | 'column-after' | 'column-before' | 'columns-to-panel';

As you can see, some fields are not required. However, depending on the type of movement, those fields are provided.

You can open the dev console for the live demo and see the emitted event.

Summary

Before defining which columns will be movable, you must allow this action by providing a property named allowColumnDragging at the grid scope. To move a column, you must define a column as draggable. You can interchange columns only from the Content panels, and you can only add data columns to the group panel. You can move columns using the Column Context Menu as well.

Complete code for this section

<form [formGroup]="moveColumnsForm"
    class="mt-2 flex flex-row flex-wrap items-center justify-around">
    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Source</mat-label>
        <mat-select formControlName="source" required
            (selectionChange)="onSourceSelectionChange($event)">
            <mat-option *ngFor="let column of gridColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of gridPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-form-field appearance="fill"
        class="flex-none w-56 m-2">
        <mat-label>Destination</mat-label>
        <mat-select formControlName="destination" required
            (selectionChange)="onDestinationSelectionChange($event)">
            <mat-option *ngFor="let column of destinationColumns" [value]="column" [disabled]="!column.draggable || column.locked">
                {{ column.headerText }}
            </mat-option>
            <mat-option *ngFor="let panel of destinationPanels" [value]="panel">
                {{ panel }}
            </mat-option>
        </mat-select>
    </mat-form-field>

    <mat-radio-group class="flex-none w-40 m-2 flex flex-col"
        #radioMethods="matRadioGroup" formControlName="method" required>
        <mat-radio-button class="my-2" value="swap-columns">swap-columns</mat-radio-button>
        <mat-radio-button class="my-2" value="column-after">column-after</mat-radio-button>
        <mat-radio-button class="my-2" value="column-before">column-before</mat-radio-button>
    </mat-radio-group>

    <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]="radioMethods.disabled || !moveColumnsForm.valid"
            (click)="onMoveColumns()">Move column(s)
        </button>

        <button mat-raised-button
            class="flex-none w-56 my-2"
            color="primary"
            [disabled]="!radioMethods.disabled"
            (click)="onMoveColumnsToPanel()">Move column(s) to Panel
        </button>
    </div>
</form>

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

    (columnMove)="onColumnMove($event)"

    [allowColumnDragging]="gridProperties.allowColumnDragging"
    allowRowGrouping

    [datasource]="dkgDataSource">

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

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

    <dike-grid-column
        fieldName="lastName"
        headerText="Surname"
        dataType="Text"
        width="150"
        minWidth="50"
        maxWidth="300"
        order="2"
        resizable
        draggable
        groupable>
    </dike-grid-column>

    <dike-grid-column
        fieldName="personalInfoGroup"
        headerText="Personal Info"
        order="1"
        resizable
        draggable>
        <dike-grid-column
            fieldName="completeNameGroup"
            headerText="Complete Name"
            draggable>
            <dike-grid-column
                fieldName="firstName"
                headerText="Name"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>

            <dike-grid-column
                fieldName="lastName"
                headerText="Surname"
                dataType="Text"
                width="150"
                minWidth="50"
                maxWidth="300"
                resizable>
            </dike-grid-column>
        </dike-grid-column>

        <dike-grid-column
            fieldName="gender"
            headerText="Gender"
            dataType="Binary"
            width="110"
            minWidth="50"
            maxWidth="200"
            resizable
            draggable
            groupable>
        </dike-grid-column>

        <dike-grid-column
            fieldName="age"
            headerText="Age"
            dataType="Numeric"
            contentAlign="center"
            width="85"
            minWidth="50"
            maxWidth="120"
            resizable
            draggable
            groupable>
        </dike-grid-column>
    </dike-grid-column>

    <dike-grid-column
        fieldName="hireDate"
        headerText="Hire Date"
        dataType="Date"
        width="125"
        order="4"
        draggable
        groupable
        panel="rightPanel">
    </dike-grid-column>
</dike-grid>

Last updated