LIDOR SYSTEMS

Advanced User Interface Controls and Components

Overview of IntegralUI TreeGrid for Angular 2

Created: 22 February 2017

Updated: 24 March 2017

IntegralUI TreeGrid is a native Angular 2 component that displays hierarchical data structures in multiple columns. You can load data on demand during run-time from local or remote data sources, and add custom HTML content in each grid cell. In following sections, you can find details about various features available in the TreeGrid component.

TreeGrid component is part of IntegralUI Web
a suite of UI Components for development of web apps

If you have any questions, don't hesitate to contact us at support@lidorsystems.com

In above demo, the grid has columns with different content: checkbox, text and image. When you click on header with check box, all rows become checked or unchecked. In addition, a click on parent row changes the check box value to its child rows. In this example, check boxes can have 2 values: checked or unchecked, but you can change this easily by providing three values.

Some columns have their content aligned to center, while others have their alignment set to left. You may also notice that expand icon is shown is second column. This is customizable, you can set which column has the expand box in your code.

By left-click on a row and move the mouse cursor, a drag drop operation will start and you can reorder rows during run-time. In this example, there are some restrictions set, that prevent drag drop of a movie from one category to another, or as a child of another movie. This is set in sample code, you can find more information in below section describing drag drop operations.

You can also select multiple rows, by holding SHIFT or CTRL key and click on specific row. For more information on supported selection types, see section below.

A performance demonstration is available at Overview of the Grid Component.

How to Use IntegralUI TreeGrid in Angular 2

In order to use the TreeGrid component in your app, you need to do the following:

  • Place the TreeGrid in your app using the iui-treegrid tag name
  • Define the template that will be used to for header, footer and rows
  • Add custom HTML elements as grid content
  • Connect the TreeGrid to your data source

//

// main.ts file

//

 

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

 

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule);

 

 

 

//

// app.module.ts file

//

 

import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

import { IntegralUIModule } from 'integralui/integralui.module';

 

@NgModule({

imports: [ BrowserModule, IntegralUIModule ],

declarations: [ AppComponent ],

bootstrap: [ AppComponent ]

})

export class AppModule { }

 

 

 

//

// app.component.ts file

//

 

import { Component } from '@angular/core';

import { IntegralUISelectionMode } from 'integralui/components/integralui.core';

import { IntegralUITreeGrid } from 'integralui/components/integralui.treegrid';

 

@Component({

selector: 'iui-app',

templateUrl: 'app.template.html',

styleUrls: ['treegrid-overview.css']

})

export class AppComponent {

// Variables used by TreeGrid component

@ViewChild('application', {read: ViewContainerRef}) applicationRef: ViewContainerRef;

 

private gridSelMode: IntegralUISelectionMode = IntegralUISelectionMode.MultiExtended;

private columns: Array<any>;

private rows: Array<any>;

 

private imageChecked = 'url(../resources/checkbox/checkbox-checked-1.png)';

private imageUnchecked = 'url(../resources/checkbox/checkbox-unchecked-1.png)';

 

// Initialize app component constructor

constructor(){

this.columns = [

{ id: 1, width: 30, fixedWidth: true },

{ id: 2, headerText: "Title", width: 400 },

{ id: 3, headerText: "Year", headerAlignment: "center", contentAlignment: "center", width: 70 },

{ id: 4, headerText: "Ratings", headerAlignment: "center", contentAlignment: "center", width: 120, fixedWidth: true },

{ id: 5, headerText: "Released", headerAlignment: "center", contentAlignment: "center", width: 130 }

];

 

this.rows = [

{

id: 1,

text: "Mystery",

cells: [{ cid: 1 }, { cid: 2, text: "Mystery" }],

rows: [

{

id: 11,

pid: 1,

text: "Inception",

cells: [{ cid: 1, value: true }, { cid: 2, text: "Inception" }, { cid: 3, text: "2010" }, { cid: 4, value: 4 }, { cid: 5, text: "16 Jul 2010" } ]

},

{

id: 12,

pid: 1,

text: "Snowpiercer",

cells: [{ cid: 1 }, { cid: 2, text: "Snowpiercer" }, { cid: 3, text: "2014" }, { cid: 4, value: 3 }, { cid: 5, text: "11 Jul 2014" } ]

},

{

id: 13,

pid: 1,

text: "Shutter Island",

cells: [ { cid: 1, value: true }, { cid: 2, text: "Shutter Island" }, { cid: 1, value: true }, { cid: 3, text: "2010" }, { cid: 4, value: 4 }, { cid: 5, text: "19 Feb 2010" } ]

},

]

},

{

id: 2,

text: "Sci-Fi",

expanded: false,

cells: [{ cid: 1 }, { cid: 2, text: "Sci-Fi" }],

rows: [

{

id: 21,

pid: 2,

text: "Gravity",

cells: [{ cid: 1 }, { cid: 2, text: "Gravity" }, { cid: 3, text: "2013" }, { cid: 4, value: 4 }, { cid: 5, text: "04 Oct 2013" } ]

},

 

. . .

 

]

},

 

. . .

 

];

}

 

private columnCheckClicked(column: any){

if (column){

let currentValue = column.value == true ? true : false;

column.value = !currentValue;

 

let list = this.grid.getFullList();

for (let i = 0; i < list.length; i++){

let cell = this.getCellWithCheckBox(list[i]);

if (cell)

cell.value = column.value;

}

}

}

 

private getCellWithCheckBox(row: any){

let found: any = null;

 

for (let j = 0; j < row.cells.length; j++){

if (row.cells[j].cid == 1){

found = row.cells[j];

break;

}

}

 

return found;

}

 

private updateChildRows(row: any, value: boolean){

if (row && row.rows){

for (let i = 0; i < row.rows.length; i++){

let cell = this.getCellWithCheckBox(row.rows[i]);

if (cell)

cell.value = value;

}

}

}

 

private checkBoxClicked(cell: any){

if (cell){

let currentValue = cell.value == true ? true : false;

cell.value = !currentValue;

 

if (cell.rid){

let row = this.grid.findRowById(cell.rid);

this.updateChildRows(row, cell.value);

}

}

}

 

private getCheckValue(obj: any){

return obj && obj.value == true ? this.imageChecked : this.imageUnchecked;

}

 

private getCellRating(cell: any): string {

return cell.value ? '../resources/stars-small-' + cell.value.toString() + '.png' : '';

}

}

//

// app.template.html file

//

 

<div #application>

<iui-treegrid [appRef]="applicationRef" [columns]="columns" [rows]="rows" [showFooter]="false" [expandColumnIndex]="1" [selectionMode]="gridSelMode" [allowDrag]="true">

<template let-column [iuiTemplate]="{ type: 'header' }">

<span *ngIf="column.id==1" class="cell-checkbox" [ngStyle]="{ 'background-image': getCheckValue(column) }" (click)="columnCheckClicked(column)"></span>

<span>{{column.headerText}}</span>

</template>

<template let-cell [iuiTemplate]="{ type: 'cell' }">

<span *ngIf="cell.cid==1" class="cell-checkbox" [ngStyle]="{ 'background-image': getCheckValue(cell) }" (click)="checkBoxClicked(cell)"></span>

<img *ngIf="cell.cid==4" class="cell-rating" src="{{getCellRating(cell)}}" />

<span class="cell-text">{{cell.text}}</span>

</template>

</iui-treegrid>

</div>

/*

treegrid-overview.css file

*/

 

.iui-treegrid

{

border: thin solid gray;

width: 800px;

height: 400px;

}

.iui-treegrid-row-cell

{

padding: 2px 0 !important;

}

.iui-treegrid-row-selected

{

color: green;

}

.cell-checkbox

{

background: url('') no-repeat 0 0;

display: inline-block;

padding: 0;

margin: 0 7px;

width: 16px;

height: 16px;

vertical-align: middle;

}

.cell-rating

{

margin: 0 15px;

vertical-align: middle;

}

.cell-text

{

text-align: center;

vertical-align: middle;

}

Each grid cell can have a different content. You can use some conditions that will distinguish content from one cell to another. In this example, the object id is used. When condition is met, the corresponding content is placed in target cell.

Note If you want to show some item with different content then other items, you can use the directive and set condition by which a different content will appear.

<template let-cell [iuiTemplate]="{ type: 'cell' }">

<span *ngIf="cell.cid==1" class="cell-checkbox" [ngStyle]="{ 'background-image': getCheckValue(cell) }" (click)="checkBoxClicked(cell)"></span>

<img *ngIf="cell.cid==4" class="cell-rating" src="{{getCellRating(cell)}}" />

<span class="cell-text">{{cell.text}}</span>

</template>

When you have a template ready, you need to link the data source with the TreeGrid using the columns and rows properties. The data source can be any array in flat or tree format.

Your data source should have columns and rows set. Each row can have cells and child rows. These objects can have different field names then the ones used by the TreeGrid component. You can find more information about this in below section of data binding.

Supported Properties

There are multiple properties available in the TreeGrid component. Here is short description of each property:

  • allowDrag - determines whether row can be dragged
  • allowDrop - determines whether row can be dropped
  • appRef - holds a reference to application view
  • columns - holds a reference to the list of column objects defined in your application component
  • controlStyle - specifies an object that holds names for custom CSS classes
  • data - specifies an object that holds data related to the TreeGrid
  • dataFields - specifies an object that map the fields names from data source to the ones used by the TreeGrid
  • expandColumnIndex - specifies the column index that will show the expand boxes
  • gridLines - specifies whether horizontal, vertical or both lines are displayed in the grid
  • rows - holds a reference to the list of row objects defined in your application component
  • selectedColumn - an object that points to the currently selected column
  • selectedRow - an object that points to the currently selected row
  • selectionMode - specifies the selection type: none, single or multiple
  • showFooter - determines whether footer is visible or not
  • showHeader - determines whether header is visible or not
  • state - specifies the state of the TreeGrid: disabled, hovered, etc.

To avoid using plain javascript manipulation of the DOM and because of the way Angular 2 is structured, in order to show a tooltip, context menu or any other popup window, a reference to the root application component is required. You can provide a value to appRef property by simply setting a variable to the root component, and then access it by calling the ViewChild with specified variable name.

// Get a reference of application view

@ViewChild('application', {read: ViewContainerRef}) applicationRef: ViewContainerRef;

<div #application>

<iui-treegrid [appRef]="applicationRef" [columns]="columns" [rows]="rows">

</iui-treegrid>

</div>

If you want to have a specified row selected initially, you can set the selectedRow property to point to the row object in your data source. You can also set whether a row will appear as collapsed initially, by specifying the expanded field value to false. By default, all rows are expanded.

Data Binding in TreeGrid

When you have a custom data source which may differ from internal data settings of the tree grid, you can use data binding which will match the names of data fields in your data source with those used by the tree grid.

By specifying the columns and rows properties to point to your data object, along with dataFields property that holds an object that maps the names of fields in the data object, you can populate the TreeGrid using any kind of custom data source.

Supported Events

When interacting with the TreeGrid component, depending on the action, a corresponding event is fired. For example, dragging a row and dropping it over another row will fire dragOver and dragDrop events, adding a row will fire rowAdding and rowAdded events, expanding it will fire the beforeExpand and afterExpand events, etc.

Here is a list of available events:

  • afterCollapse - occurs after row is collapsed
  • afterExpand - occurs after row is expanded
  • afterSelect - occurs after column or row is selected
  • beforeCollapse - occurs before row is collapsed
  • beforeExpand - occurs before row is expanded
  • beforeSelect - occurs before column or row is selected
  • columnAdded - occurs when new column is added to the TreeGrid
  • columnAdding - occurs before column is added
  • columnsCleared - occurs when all columns are removed from the TreeGrid
  • columnRemoved - occurs when columnis removed from the TreeGrid
  • columnRemoving - occurs before columnis removed
  • dragDrop - occurs when row is dropped over target row or empty space
  • dragEnter - occurs when dragged row enters the TreeGrid space
  • dragLeave - occurs when dragged row leaves the TreeGrid space
  • dragOver - occurs when row is dragged over TreeGrid space
  • rowAdded - occurs when new row is added to the TreeGrid
  • rowAdding - occurs before row is added
  • rowsCleared - occurs when all rows are removed from the TreeGrid
  • rowClick - occurs when row is clicked
  • rowDblClick - occurs when row is double-clicked
  • rowRemoved - occurs when row is removed from the TreeGrid
  • rowRemoving - occurs before row is removed
  • scrollPosChanged - occurs when position of vertical or horizontal scrollbar has changed
  • selectionChanged - occurs when currently selected row has changed

By handling these events in your code, you can add custom actions that may alter the default behavior of the TreeGrid component. For example, by handling the dragOver event, you can exclude a specified row as a target during drag drop operation, by setting a condition.

How to Add/Remove Rows Dynamically

In some cases, you may need to add new rows or remove existing rows in the TreeGrid during run-time. For this purpose, there are built-in methods available that allow you to change the structure of the TreeGrid:

//

// app.component.ts file

//

 

export class AppComponent {

// An Array object that holds all column objects shown in TreeGrid

private columns: Array;

// An Array object that holds all row objects shown in TreeGrid

private rows: Array;

// Holds the number of rows created

private rowCount: number = 0;

 

// Get a reference to the TreeGrid component using a variable set in HTML

@ViewChild('treegrid') treegrid: IntegralUITreeGrid;

 

// Initialize rows in component constructor

constructor(){

this.rows = [];

}

 

// Adds a new row to the end of the TreeGrid

addRow(){

let row = {

text : 'Row ' + (this.rowCount+1).toString(),

cells: []

};

 

for (let j = 0; j < this.columns.length; j++)

row.cells.push({ text: "Item" + (this.rowCount+1).toString() + j });

 

this.treegrid.addRow(row);

 

this.rowCount++;

}

 

// Fired whenever a new row is added to the TreeGrid

rowAddedEvent(e){

if (e.row)

console.log("rowAdded: " + e.row.text);

}

}

 

bootstrap(AppComponent);

//

// app.template.html file

//

 

<button (click)="addRow()">Add Row<button><br /><br />

<iui-treegrid [columns]="columns" [rows]="rows" (rowAdded)="rowAddedEvent($event)" #treegrid>

<template let-column [iuiTemplate]="{ type: 'header' }">

{{column.headerText}}

</template>

<template let-cell [iuiTemplate]="{ type: 'cell' }">

{{cell.text}}<

</template>

</iui-treegrid>

In above code, we are adding a new row with some rows using the addRow method. To access this method, at first we need to get a reference to the TreeGrid component. In HTML we are adding the #treegrid variable, which is used to locate the TreeGrid within your application component.

Related: Add Rows on Demand in Angular TreeGrid

After you have a reference to the TreeGrid component, you can get access to all public methods available:

  • addColumn - inserts a new column
  • addRow - inserts a new row at the end of TreeGrid
  • clearColumns - removes all columns from the TreeGrid
  • clearRows - removes all rows from the TreeGrid
  • collapse - collapses all or specified row
  • expand - expands all or specified row
  • findRowById - searches through tree hierarchy for an row that matches specified id
  • findRowByText - searches through tree hierarchy for an row that matches specified text
  • getFullList - retrieves a flat list of tree hierarchy of all rows
  • getRowParent - returns a row that is parent of specified row
  • insertColumnAfter - inserts a new column in a position after specified column
  • insertColumnBefore - inserts a new column in a position before specified column
  • insertRowAfter - inserts a new column at specified position
  • insertRowBefore - inserts a new row in a position after specified row
  • insertRowAt - inserts a new row at specified position
  • isHorScrollVisible - returns a value stating whether the horizontal scrollbar is visible or not
  • isRowExpanded - returns a value stating whether the specified row is expanded or collapsed
  • isVerScrollVisible - returns a value stating whether the vertical scrollbar is visible or not
  • refresh - updates the appearance of the TreeGrid
  • removeColumn - removes the specified column from the TreeGrid
  • removeRow - removes the specified row from the TreeGrid
  • scrollPos - gets or sets the current scrolling position
  • selectRows - select a set of rows from code
  • toggle - changes between expanded and collapsed state for all or specified row
  • updateLayout - updates the TreeGrid layout in whole
  • updateView - updates only the current view of the TreeGrid
  • Advanced Drag and Drop Operations

    TreeGrid component comes with advanced drag drop that allows you to reorder rows in tree hierarchy by simply dragging one or multiple rows from one place to another within the same or other components.

    During this process, events are fired that can help you to add custom actions that may alter the default built-in drag drop functionality. In each event, you can set up custom conditions that can prevent or allow drag drop in special cases, depending on your application requirements.

    Whenever a row is dragged, a dragging window will appear showing the target row and position at which row can be dropped. There are four possible positions:

    • up arrow - states that row will be dropped above target row
    • arc arrow - states that row will be dropped as a child of target row
    • down arrow - states that row will be dropped below target row
    • down arrow with a line - states that row will be dropped at the end of tree hierarchy

    In this demo, you can only reorder child rows within its parent row, or you can reorder root rows. You cannot drag and drop a row as a child of another row. These conditions are set to because of the sample data, a movie cannot become a child of another movie.

    gridDragOver(e){

    if (e.targetRow){

    switch (e.dropPos){

    case 0: // arc arrow

    // Cancel all drag drop operations when dragged row is about to be dropped as a child of target row

    e.cancel = true;

    break;

     

    case 1: // up arrow

    // If dragged row is a child and its dragged over root row

    if (!e.dragRow.pid && e.targetRow.pid)

    e.cancel = true;

    // If dragged row is a root row and its dragged over child row

    else if (e.dragRow.pid && !e.targetRow.pid)

    e.cancel = true;

    break;

     

    case 2: // down arrow

    // If dragged row is a child and its dragged over root row

    if (!e.dragRow.pid && e.targetRow.pid)

    e.cancel = true;

    // If dragged row is a root row and its dragged over child row

    else if (e.dragRow.pid && !e.targetRow.pid)

    e.cancel = true;

    break;

    }

    }

    // down arrow with a line

    // Cancel all drag drop operations if dragged row is a child and is dragged over empty space at the end of the TreeGrid

    else if (e.dragRow.pid)

    e.cancel = true;

    }

    <div #application>

    <iui-treegrid [columns]="columns" [rows]="rows" [allowDrag]="true" (dragOver)="gridDragOver($event)">

    <template let-column [iuiTemplate]="{ type: 'header' }">

    {{column.headerText}}

    </template>

    <template let-cell [iuiTemplate]="{ type: 'cell' }">

    {{cell.text}}<

    </template>

    </iui-treegrid>

    </div>

    By default, during drag and drop rows are moved from their original position to a new one. In some cases, instead of moving you may need to create a copy of dragged rows. Copy operation is already built-in, you only need to press and hold the SHIFT key, when row is dropped. The dragging window will change its icon showing a + sign. This states that copy of dragged row will drop at specified position.

    How to Select Multiple Rows

    By default, a single selection mode is active, which means that only one row can become selected at one time. You can change this by setting the selectionMode property to a different value. There are four values supported:

    • IntegralUISelectionMode.None - selection is disabled
    • IntegralUISelectionMode.One - only one row can become selected
    • IntegralUISelectionMode.MultiSimple - multiple rows can become selected without keyboard
    • IntegralUISelectionMode.MultiExtended - multiple rows can become selected by holding CTRL or SHIFT key and mouse clicks

    You can also select multiple rows using the selectRows method. You can call this method from your app code and select rows manually without user interaction.

    selectMulti(){

    // Create a list of rows that you want to be selected

    let list = [];

     

    // Add first row to the list

    list.push(this.rows[0]);

     

    // Add child rows of the first row starting from second child

    if (this.rows[0].rows){

    for (let i = 1; i < this.rows[0].rows.length; i++){

    let childRow = this.rows[0].rows[i];

    list.push(childRow);

    }

    }

     

    // Add third row to the list

    list.push(this.rows[2]);

     

    // Add this list as argument to the selectRows method

    this.treegrid.selectRows(list);

    }

    How to Display Thousands of Rows into the TreeGrid

    There is already built-in virtualization in the TreeGrid, which allows you to display hundreds of thousands of rows at one time. The only limit here is how much data the browser can handle, but for sure, you can display for example 10,000 rows in 100 columns.

    The TreeGrid is optimized to work with large data sets. You can load your data set initially into the TreeGrid, and update only small portion on your server when required. All work is done on the client side, which increases overall user interaction.

    You can also choose to load data on demand. Just before a row expands, a new data is retrieved from the server and the TreeGrid is updated. This process requires very little time to execute, in most cases is instantly.

    How to Customize the TreeGrid Appearance

    Each part of IntegralUI TreeGrid component is fully customizable. There are different CSS classes for each component part. Although changing the attributes of built-in classes is possible, you can completely override them using the controlStyle property.

    The controlStyle property accpets an object that holds all or partial list of CSS class names that will override the default CSS class names. For each component part, a different CSS class governs its appearance. This allows you to specify a set of different CSS classes for each component part and alter the appearance of the TreeGrid in whole. In this way, you can make it more suitable for your application requirements.

    Conclusion

    IntegralUI TreeGrid component allows you to display hierarchical data structures in multiple columns. It has built-in virtualization that allows you to work with thousands of rows on client side. You can populate the TreeGrid using custom data source, locally or remotely. Supports drag drop operations, which can be further customized on your side by handling events.

    By using templates, you can add any custom HTML elements or Angular 2 components in column header, footer and each row cell individually. With use of conditions like ngIf directive, you can have different cells with different content.

    The TreeGrid component is part of IntegralUI Web.

    Did you Like this Article?


    Enter your e-mail address below and you will receive latest articles as well as news on upcoming events and special offers.