LIDOR SYSTEMS

Advanced User Interface Controls and Components

DropDown List in Tree Grid Cells for Angular

Created: 18 August 2017

By default, all cells in TreeGrid component displays a simple text. To display custom content in grid cell, you need to add custom HTML elements or other Angular component to the cell template. In this article, you will learn how to add a dropdown checked list to the grid cells and change their value based on selected option.

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

As this demo shows, all cells in Genre column display an arrow on the right side. When cell is clicked a dropdown list will popup, showing items that you can check. To confirm selection, click on any item in the list. Once the dropdown window closes, the cell value is updated.

The dropdown list is sorted in a way that at first shows all checked items for that specific cell, following by all other items in ascending order. If you click outside of the dropdown window or press the ESCAPE key, the selection is cancelled, and the cell value will remain the same.

How to Create a DropDown List with CheckBoxes

At first, you need to create a checked list of items. For this purpose we are using the <ul>, <li> HTML elements where each item has a checkbox and a label. The template looks like this:

<ng-template let-obj [iuiTemplate]="{ type: 'dropdown' }">
    <ul class="dropdown-list" [ngStyle]="{ width: dropDownWidth + 'px'}" (mousedown)="listMouseDown($event)">
        <li *ngFor="let item of dropdownItems" (mousedown)="itemSelected(cell, item)">
            <span class="cell-checkbox" [ngStyle]="{ 'background-image': getItemValue(item) }" style="margin-top:-1px" (mousedown)="itemChecked($event, cell, item)"></span>
            <span>{{item.text}}</span>
        </li>
    </ul>
</ng-template>
                            
/* DropDown Cell */
.dropdown-list
{
    cursor: default;
    margin: 0;
    overflow: auto;
    padding: 0;
    list-style-type: none;
    white-space: nowrap;
    width: 150px;
    height: 140px;
}
.dropdown-list li
{
    padding: 5px;
}
.dropdown-list li:hover
{
    background: #e5e5e5;
}
.cell-checkbox
{
    background: url('') no-repeat 0 0;
    display: inline-block;
    padding: 0;
    margin: 5px 7px 0 7px;
    width: 16px;
    height: 16px;
    vertical-align: middle;
}

                            

Checkbox is represented by a <span> element, where for its background a checkbox icon is used. Depending on item value, checkbox is checked or unchecked.

When item label is clicked, the selection is confirmed and the dropdown list is closed.

Apply the DropDown Editor to Grid Cells

To show a popup window, you can use IntegralUIDropDown directive or any other Angular component. In case of the DropDown directive, to show a dropdown list you need to apply the previous template as content to the directive.

<div [iuiDropDown]="cell.dropdown" (dropDownOpen)="onDropDownOpen($event, cell)" #dropdown>
    <div class="treegrid-ccmb-adv-item-drop-mark"><span></span></div>
    <span class="treegrid-ccmb-adv-item-label">{{cell.text}}</span> 
    <ng-template let-obj [iuiTemplate]="{ type: 'dropdown' }">
        <ul class="dropdown-list" [ngStyle]="{ width: dropDownWidth + 'px'}" (mousedown)="listMouseDown($event)">
            <li *ngFor="let item of dropdownItems" (mousedown)="itemSelected(cell, item)">
                <span class="cell-checkbox" [ngStyle]="{ 'background-image': getItemValue(item) }" style="margin-top:-1px" (mousedown)="itemChecked($event, cell, item)"></span>
                <span>{{item.text}}</span>
            </li>
        </ul>
    </ng-template>
</div>
                            

Because dropdown is applied to many grid cells, you need to make sure each cell display its value and a dropdown icon. In this way, the user will know that when cell is clicked, it will open a dropdown window.

<div [iuiDropDown]="cell.dropdown" (dropDownOpen)="onDropDownOpen($event, cell)" #dropdown>
    <div class="treegrid-ccmb-adv-item-drop-mark"><span></span></div>
    <span class="treegrid-ccmb-adv-item-label">{{cell.text}}</span>
    <ng-template let-obj [iuiTemplate]="{ type: 'dropdown' }">
        <ul class="dropdown-list" [ngStyle]="{ width: dropDownWidth + 'px'}" (mousedown)="listMouseDown($event)">
            <li *ngFor="let item of dropdownItems" (mousedown)="itemSelected(cell, item)">
                <span class="cell-checkbox" [ngStyle]="{ 'background-image': getItemValue(item) }" style="margin-top:-1px" (mousedown)="itemChecked($event, cell, item)"></span>
                <span>{{item.text}}</span>
            </li>
        </ul>
    </ng-template>
</div>
                            
.treegrid-ccmb-adv-item-drop-mark
{
    float: right;
}
.treegrid-ccmb-adv-item-drop-mark span
{
    background: url(app/integralui/resources/icons.png) -144px -80px no-repeat;
    display: inline-block;
    opacity: 0.5;
    overflow: hidden;
    padding: 0 !important;
    margin: 4px 2px 0 0;
    width: 16px;
    height: 16px;
    vertical-align: middle;
}
                            

Each cell that has a dropdown directive applied, need to have a object field with the following settings:

  • appRef - holds a reference of top application component, required in order for popup window to appear
  • adjustment - depending on Grid layout, there may be a need for position adjustments on dropdown window
  • data - holds a reference to the cell object or any custom data that you may need in DropDown directive

When Grid is created, you can cycle through all rows and apply this object to all cells that have a DropDown directive attached. In this example, because multiple items can be checked from the dropdown list, the cell value is represented by a set of string values separated by commas. The code looks like this:

ngAfterViewInit(){
    // Set a dropdown for row cells
    let list = this.treegrid.getFullList();
    for (let i = 0; i < list.length; i++)
        for (let j = 0; j < list[i].cells.length; j++){
            let cell: any = list[i].cells[j];

            if (cell.cid == 4){
                cell.dropdown = {
                    appRef: this.applicationRef,
                    adjustment: { top: 0, left: -3 },
                    data: cell
                }

                cell.value = [];
                let selGenres = cell.text.split(', ');
                for (let j = 0; j < selGenres.length; j++){
                    let foundItem = this.findGenre(selGenres[j]);
                    if (foundItem)
                        cell.value.push(foundItem);
                }
            }
        }
}

findGenre(text: string){
    let found: any = null;
    for (var i = 0; i < this.dropdownItems.length; i++){
        if (this.dropdownItems[i].text == text){
            found = this.dropdownItems[i];
            break;
        }
    }

    return found;
}
                            

Now you can apply the cell object with settings to the DropDown directive, using the directive name as a property.

<div [iuiDropDown]="cell.dropdown" (dropDownOpen)="onDropDownOpen($event, cell)" #dropdown>
    <div class="treegrid-ccmb-adv-item-drop-mark"><span></span></div>
    <span class="treegrid-ccmb-adv-item-label">{{cell.text}}</span> 
    <ng-template let-obj [iuiTemplate]="{ type: 'dropdown' }">
        <ul class="dropdown-list" [ngStyle]="{ width: dropDownWidth + 'px'}" (mousedown)="listMouseDown($event)">
            <li *ngFor="let item of dropdownItems" (mousedown)="itemSelected(cell, item)">
                <span class="cell-checkbox" [ngStyle]="{ 'background-image': getItemValue(item) }" style="margin-top:-1px" (mousedown)="itemChecked($event, cell, item)"></span>
                <span>{{item.text}}</span>
            </li>
        </ul>
    </ng-template>
</div>
                            

When cell is clicked, the dropdown window will popup, however the list will appear the same for all cells. In order to show checked items first, based on cell value that is previously selected options, you need to handle the dropDownOpen event for the DropDown directive. This allows you to modify how the dropdown list will appear based on which cell is clicked.

onDropDownOpen(e: any, cell: any){
    this.isItemSelected = false;

    if (cell){
        this.dropDownWidth = this.getCellWidth(cell);

        let cellItems = cell.value;
        if (cellItems){
            this.resetList();

            for (let i = 0; i < cellItems.length; i++)
                cellItems[i].checked = true;
        }

        this.showCheckedFirst();
    }
}

// Calculates the width of tree grid cell
getCellWidth(cell: any){
    let cellWidth: number = 100;

    for (let j = 0; j < this.columns.length; j++){
        if (this.columns[j].id == cell.cid){
            cellWidth = this.getColumnWidth(this.columns[j]) + 3; 
            break;
        }
    }

    return cellWidth;
}

protected getColumnWidth(column: any): number {
    return column && column.width != undefined ? column.width : 100;
}
                            
<div [iuiDropDown]="cell.dropdown" (dropDownOpen)="onDropDownOpen($event, cell)" #dropdown>
    <div class="treegrid-ccmb-adv-item-drop-mark"><span></span></div>
    <span class="treegrid-ccmb-adv-item-label">{{cell.text}}</span> 
    <ng-template let-obj [iuiTemplate]="{ type: 'dropdown' }">
        <ul class="dropdown-list" [ngStyle]="{ width: dropDownWidth + 'px'}" (mousedown)="listMouseDown($event)">
            <li *ngFor="let item of dropdownItems" (mousedown)="itemSelected(cell, item)">
                <span class="cell-checkbox" [ngStyle]="{ 'background-image': getItemValue(item) }" style="margin-top:-1px" (mousedown)="itemChecked($event, cell, item)"></span>
                <span>{{item.text}}</span>
            </li>
        </ul>
    </ng-template>
</div>
                            

In this case, the checked items appear first followed by a sorted list of unchecked items.

// Show checked items first and apply sorting
showCheckedFirst(){
    let checkedList: Array = [];
    let uncheckedList: Array = [];

    for (let i = 0; i < this.dropdownItems.length; i++){
        if (this.dropdownItems[i].checked)
            checkedList.push(this.dropdownItems[i]);
        else
            uncheckedList.push(this.dropdownItems[i]);
    }

    checkedList.sort(this.sortComparer);
    uncheckedList.sort(this.sortComparer);

    this.dropdownItems.length = 0;
    for (let i = 0; i < checkedList.length; i++)
        this.dropdownItems.push(checkedList[i]);
    for (let i = 0; i < uncheckedList.length; i++)
       this.dropdownItems.push(uncheckedList[i]);
}

sortComparer(firstItem: any, secondItem: any){
    let x = firstItem.text != undefined ? firstItem.text : null;
    let y = secondItem.text != undefined ? secondItem.text : null;

    if (x < y)
        return -1;
    else if (x > y)
        return 1;

    return 0;
}                            

Put All Together

Finally, the complete code that adds a drodown checked list to the cells in TreeGrid component is available here:

// 
// app.module.ts
//

import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { IntegralUIModule } from './integralui/integralui.module';

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

@NgModule({
    imports:      [ 
          BrowserModule, 
          FormsModule, 
          IntegralUIModule
    ],
    declarations: [ 
        AppComponent, 
    ],
    bootstrap: [ AppComponent ]
})
export class AppModule { }

// 
// app.component.ts
//

import { Component, ViewContainerRef, ViewChild, ViewEncapsulation } from '@angular/core';
import { IntegralUITreeGrid } from './integralui/components/integralui.treegrid';

@Component({
    selector: 'iui-app',
    templateUrl: 'app.template.html',
    styleUrls: ['sample-styles.css'],
    encapsulation: ViewEncapsulation.None
})
export class AppComponent {
    // Get a reference to the application root component
    @ViewChild('application', {read: ViewContainerRef}) applicationRef: ViewContainerRef;
    // Get a reference to the TreeGrid component
    @ViewChild('treegrid') treegrid: IntegralUITreeGrid;

    // An array that holds all options in the comboo box
    public dropdownItems: Array;
    public dropDownWidth: number = 150;

    // TreeGrid  settings
    public columns: Array;
    public rows: Array;

    // Control Style
    public treegridStyle: any = {
        general: {
            normal : 'treegrid-ccmb-adv-normal'
        }
    }

    private imageChecked: string = 'url(app/integralui/resources/checkbox/checkbox-checked-1.png)';
    private imageUnchecked: string = 'url(app/integralui/resources/checkbox/checkbox-unchecked-1.png)';

    constructor(){
        // Options to choose from
        this.dropdownItems = [
            { text: "Action", checked: false },
            { text: "Adventure", checked: false },
            { text: "Animation", checked: false },
            { text: "Biography", checked: false },
            { text: "Comedy", checked: false },
            { text: "Crime", checked: false },
            { text: "Drama", checked: false },
            { text: "Fantasy", checked: false },
            { text: "Horror", checked: false },
            { text: "Mystery", checked: false },
            { text: "Sci-Fi", checked: false },
            { text: "Thriller", checked: false },
            { text: "Western", checked: false }
        ];

        // Add columns and rows for the TreeGrid
        this.columns = [
            { id: 1, headerText: "Title", width: 280 },
            { id: 4, headerText: "Genre", headerAlignment: "center", contentAlignment: "center", width: 270 },
            { id: 5, headerText: "Ratings", headerAlignment: "center", contentAlignment: "center", width: 70 },
            { id: 6, headerText: "Released", headerAlignment: "center", contentAlignment: "center", width: 120 }
        ];

        this.rows = [
            { 
                id: 1,
                text: "2010",
                cells: [{ cid: 1, text: "2010" }],
                rows: [
                    { id: 11, pid: 1, text: "Inception", cells: [{ cid: 1, text: "Inception" }, { cid: 2, value: true }, { cid: 3, text: "2010" }, { cid: 4, text: "Action, Mystery, Sci-Fi" }, { cid: 5, text: "8.8" }, { cid: 6, text: "16 Jul 2010" }] },
                    { id: 12, pid: 1, text: "Toy Story 3", cells: [{ cid: 1, text: "Toy Story 3" }, { cid: 2, value: true }, { cid: 3, text: "2010" }, { cid: 4, text: "Animation, Adventure, Comedy" }, { cid: 5, text: "8.4" }, { cid: 6, text: "18 Jun 2010" } ] },
                    { id: 13, pid: 1, text: "The Town", cells: [{ cid: 1, text: "The Town" }, { cid: 2 }, { cid: 3, text: "2010" }, { cid: 4, text: "Crime, Drama, Thriller" }, { cid: 5, text: "7.6" }, { cid: 6, text: "17 Sep 2010" }] },
                    { id: 14, pid: 1, text: "The Social Network", cells: [{ cid: 1, text: "The Social Network" }, { cid: 2, value: true }, { cid: 3, text: "2010" }, { cid: 4, text: "Biography, Drama" }, { cid: 5, text: "7.8" }, { cid: 6, text: "01 Oct 2010" }] },
                    { id: 15, pid: 1, text: "Black Swan", cells: [{ cid: 1, text: "Black Swan" }, { cid: 2 }, { cid: 3, text: "2010" }, { cid: 4, text: "Drama, Mystery, Thriller" }, { cid: 5, text: "8.0" }, { cid: 6, text: "17 Dec 2010" }] },
                    { id: 16, pid: 1, text: "Shutter Island", cells: [{ cid: 1, text: "Shutter Island" }, { cid: 2, value: true }, { cid: 3, text: "2010" }, { cid: 4, text: "Mystery, Thriller" }, { cid: 5, text: "8.1" }, { cid: 6, text: "19 Feb 2010" }] }
                ]
            },
            { 
                id: 2,
                text: "2012",
                cells: [{ cid: 1, text: "2012" }],
                rows: [
                    { id: 21, pid: 2, text: "Django Unchained", cells: [{ cid: 1, text: "Django Unchained" }, { cid: 2, value: true }, { cid: 3, text: "2012" }, { cid: 4, text: "Western" }, { cid: 5, text: "8.5" }, { cid: 6, text: "25 Dec 2012" }] },
                    { id: 22, pid: 2, text: "Prometheus", cells: [{ cid: 1, text: "Prometheus" }, { cid: 2, value: true }, { cid: 3, text: "2012" }, { cid: 4, text: "Adventure, Mystery, Sci-Fi" }, { cid: 5, text: "7.0" }, { cid: 6, text: "08 Jun 2012" } ] },
                    { id: 23, pid: 2, text: "The Avengers", cells: [{ cid: 1, text: "The Avengers" }, { cid: 2 }, { cid: 3, text: "2012" }, { cid: 4, text: "Action, Adventure, Sci-Fi" }, { cid: 5, text: "8.2" }, { cid: 6, text: "04 May 2012" }] },
                    { id: 24, pid: 2, text: "The Dark Knight Rises", cells: [{ cid: 1, text: "The Dark Knight Rises" }, { cid: 2, value: true }, { cid: 3, text: "2012" }, { cid: 4, text: "Action, Thriller" }, { cid: 5, text: "8.5" }, { cid: 6, text: "20 Jul 2012" }] }
                ]
            },
            { 
                id: 3,
                text: "2013",
                cells: [{ cid: 1, text: "2013" }],
                rows: [
                    { id: 31, pid: 3, text: "Gravity", cells: [{ cid: 1, text: "Gravity" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Sci-Fi, Thriller" }, { cid: 5, text: "7.9" }, { cid: 6, text: "04 Oct 2013" }] },
                    { id: 32, pid: 3, text: "The Wolf of Wall Street", cells: [{ cid: 1, text: "The Wolf of Wall Street" }, { cid: 2, value: true }, { cid: 3, text: "2013" }, { cid: 4, text: "Biography, Comedy, Crime" }, { cid: 5, text: "8.2" }, { cid: 6, text: "25 Dec 2013" } ] },
                    { id: 33, pid: 3, text: "The Conjuring", cells: [{ cid: 1, text: "The Conjuring" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Horror" }, { cid: 5, text: "7.5" }, { cid: 6, text: "19 Jul 2013" }] },
                    { id: 34, pid: 3, text: "Blue Jasmine", cells: [{ cid: 1, text: "Blue Jasmine" }, { cid: 2, value: true }, { cid: 3, text: "2013" }, { cid: 4, text: "Drama" }, { cid: 5, text: "7.3" }, { cid: 6, text: "23 Aug 2013" }] },
                    { id: 35, pid: 3, text: "Prisoners", cells: [{ cid: 1, text: "Prisoners" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Crime, Drama, Mystery" }, { cid: 5, text: "8.1" }, { cid: 6, text: "20 Sep 2013" }] },
                    { id: 36, pid: 3, text: "American Hustle", cells: [{ cid: 1, text: "American Hustle" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Crime, Drama" }, { cid: 5, text: "7.3" }, { cid: 6, text: "20 Dec 2013" }] },
                    { id: 37, pid: 3, text: "Frozen", cells: [{ cid: 1, text: "Frozen" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Animation, Adventure, Comedy" }, { cid: 5, text: "7.7" }, { cid: 6, text: "27 Nov 2013" }] },
                    { id: 38, pid: 3, text: "Rush", cells: [{ cid: 1, text: "Rush" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Action, Biography, Drama" }, { cid: 5, text: "8.2" }, { cid: 6, text: "27 Sep 2013" }] },
                    { id: 39, pid: 3, text: "This Is the End", cells: [{ cid: 1, text: "This Is the End" }, { cid: 2 }, { cid: 3, text: "2013" }, { cid: 4, text: "Comedy, Fantasy" }, { cid: 5, text: "6.7" }, { cid: 6, text: "12 Jun 2013" }] }
                ]
            },
            { 
                id: 4,
                text: "2014",
                cells: [{ cid: 1, text: "2014" }],
                rows: [
                    { id: 41, pid: 4, text: "Nightcrawler", cells: [{ cid: 1, text: "Nightcrawler" }, { cid: 2, value: true }, { cid: 3, text: "2014" }, { cid: 4, text: "Crime, Thriller" }, { cid: 5, text: "7.9" }, { cid: 6, text: "31 Oct 2014" }] },
                    { id: 42, pid: 4, text: "Locke", cells: [{ cid: 1, text: "Locke" }, { cid: 2 }, { cid: 3, text: "2014" }, { cid: 4, text: "Drama" }, { cid: 5, text: "7.1" }, { cid: 6, text: "29 May 2014" }] },
                    { id: 43, pid: 4, text: "Snowpiercer", cells: [{ cid: 1, text: "Snowpiercer" }, { cid: 2 }, { cid: 3, text: "2014" }, { cid: 4, text: "Action, Mystery, Sci-Fi" }, { cid: 5, text: "7.0" }, { cid: 6, text: "11 Jul 2014" }] },
                    { id: 44, pid: 4, text: "Edge of Tomorrow", cells: [{ cid: 1, text: "Edge of Tomorrow" }, { cid: 2, value: true }, { cid: 3, text: "2014" }, { cid: 4, text: "Action, Adventure, Mystery" }, { cid: 5, text: "7.9" }, { cid: 6, text: "06 Jun 2014" }] },
                    { id: 45, pid: 4, text: "Interstellar", cells: [{ cid: 1, text: "Interstellar" }, { cid: 2 }, { cid: 3, text: "2014" }, { cid: 4, text: "Adventure, Drama, Sci-Fi" }, { cid: 5, text: "8.7" }, { cid: 6, text: "07 Nov 2014" }] },
                    { id: 46, pid: 4, text: "Interstellar", cells: [{ cid: 1, text: "Dawn of the Planet of the Apes" }, { cid: 2, value: true }, { cid: 3, text: "2014" }, { cid: 4, text: "Action, Drama, Sci-Fi" }, { cid: 5, text: "7.7" }, { cid: 6, text: "11 Jul 2014" }] }
                ]
            },
        ];
    }

    ngAfterViewInit(){
        // Set a dropdown for row cells
        let list = this.treegrid.getFullList();
        for (let i = 0; i < list.length; i++)
            for (let j = 0; j < list[i].cells.length; j++){
                let cell: any = list[i].cells[j];

                if (cell.cid == 4){
                    cell.dropdown = {
                        appRef: this.applicationRef,
                        adjustment: { top: 0, left: -3 },
                        data: cell
                    }

                    cell.value = [];
                    let selGenres = cell.text.split(', ');
                    for (let j = 0; j < selGenres.length; j++){
                        let foundItem = this.findGenre(selGenres[j]);
                        if (foundItem)
                            cell.value.push(foundItem);
                    }
                }
            }
    }

    findGenre(text: string){
        let found: any = null;
        for (var i = 0; i < this.dropdownItems.length; i++){
            if (this.dropdownItems[i].text == text){
                found = this.dropdownItems[i];
                break;
            }
        }

        return found;
    }

    // DropDown Cell ---------------------------------------------------------------------

    private isItemSelected: boolean = false;

    // Prevent clicks in the list scrollbar area to close the dropdown window
    listMouseDown(e: any){
        if (!this.isItemSelected)
            e.stopPropagation();
    }

    private getItemValue(item: any){
        return item && item.checked == true ? this.imageChecked : this.imageUnchecked;
    }

    itemChecked(e: any, cell: any, item: any){
        let currentValue = item.checked == true ? true : false;
        item.checked = !currentValue;

        e.stopPropagation();
    }

    itemSelected(cell: any, item: any){
        if (cell){
            let list: Array = [];
            for (let i = 0; i < this.dropdownItems.length; i++){
                if (this.dropdownItems[i].checked)
                    list.push(this.dropdownItems[i]);
            }

            cell.text = '';
            for (let i = 0; i < list.length; i++)
                cell.text += list[i].text + ", ";

            if (cell.text != '')
                cell.text = cell.text.substring(0, cell.text.length - 2);

            cell.value = list;

            this.isItemSelected = true;
        }
    }

    // Calculates the width of treegrid cell
    protected getColumnWidth(column: any): number {
        return column && column.width != undefined ? column.width : 100;
    }

    getCellWidth(cell: any){
        let cellWidth: number = 100;

        for (let j = 0; j < this.columns.length; j++){
            if (this.columns[j].id == cell.cid){
                cellWidth = this.getColumnWidth(this.columns[j]) + 3; 
                break;
            }
        }

        return cellWidth;
    }

    onDropDownOpen(e: any, cell: any){
        this.isItemSelected = false;

        if (cell){
            this.dropDownWidth = this.getCellWidth(cell);

            let cellItems = cell.value;
            if (cellItems){
                this.resetList();

                for (let i = 0; i < cellItems.length; i++)
                    cellItems[i].checked = true;
            }

            this.showCheckedFirst();
        }
    }

    resetList(){
        for (let i = 0; i < this.dropdownItems.length; i++)
            this.dropdownItems[i].checked = false;
    }

    // Show checked items first and apply sorting
    showCheckedFirst(){
        let checkedList: Array = [];
        let uncheckedList: Array = [];

        for (let i = 0; i < this.dropdownItems.length; i++){
            if (this.dropdownItems[i].checked)
                checkedList.push(this.dropdownItems[i]);
            else
                uncheckedList.push(this.dropdownItems[i]);
        }

        checkedList.sort(this.sortComparer);
        uncheckedList.sort(this.sortComparer);

        this.dropdownItems.length = 0;
        for (let i = 0; i < checkedList.length; i++)
            this.dropdownItems.push(checkedList[i]);
        for (let i = 0; i < uncheckedList.length; i++)
           this.dropdownItems.push(uncheckedList[i]);
    }
        
    sortComparer(firstItem: any, secondItem: any){
        let x = firstItem.text != undefined ? firstItem.text : null;
        let y = secondItem.text != undefined ? secondItem.text : null;

        if (x < y)
            return -1;
        else if (x > y)
            return 1;

        return 0;
    }
}
                            
<div #application>
<iui-treegrid [appRef]="applicationRef" [controlStyle]="treegridStyle" [columns]="columns" [rows]="rows" [showFooter]="false" [rowHeight]="25" #treegrid>
    <ng-template let-column [iuiTemplate]="{ type: 'header' }">
        {{column.headerText}}
    </ng-template>
    <ng-template let-cell [iuiTemplate]="{ type: 'cell' }">
        <span [ngSwitch]="cell.cid">
            <div *ngSwitchCase="4">
                <div [iuiDropDown]="cell.dropdown" (dropDownOpen)="onDropDownOpen($event, cell)" #dropdown>
                    <div class="treegrid-ccmb-adv-item-drop-mark"><span></span></div>
                    <span class="treegrid-ccmb-adv-item-label">{{cell.text}}</span> 
                    <ng-template let-obj [iuiTemplate]="{ type: 'dropdown' }">
                        <ul class="dropdown-list" [ngStyle]="{ width: dropDownWidth + 'px'}" (mousedown)="listMouseDown($event)">
                            <li *ngFor="let item of dropdownItems" (mousedown)="itemSelected(cell, item)">
                                <span class="cell-checkbox" [ngStyle]="{ 'background-image': getItemValue(item) }" style="margin-top:-1px" (mousedown)="itemChecked($event, cell, item)"></span>
                                <span>{{item.text}}</span>
                            </li>
                        </ul>
                    </ng-template>
                </div>
            </div>
            <div *ngSwitchDefault style="display:inline-block">
                <span class="treegrid-ccmb-adv-item-label">{{cell.text}}</span>
            </div>
        </span>
    </ng-template>
</iui-treegrid>
</div>
                            
/* TreeGrid settings */
.treegrid-ccmb-adv-normal
{
    width: 800px;
    height: 375px;
}
.treegrid-ccmb-adv-normal .iui-treegrid-expand-box
{
    margin-top: -3px;
}
.treegrid-ccmb-adv-item-label
{
    display: inline-block;
    margin-top: 3px;
    padding: 2px 0;
}

/* DropDown Cell */
.dropdown-list
{
    cursor: default;
    margin: 0;
    overflow: auto;
    padding: 0;
    list-style-type: none;
    white-space: nowrap;
    width: 150px;
    height: 140px;
}
.dropdown-list li
{
    padding: 5px;
}
.dropdown-list li:hover
{
    background: #e5e5e5;
}
.cell-checkbox
{
    background: url('') no-repeat 0 0;
    display: inline-block;
    padding: 0;
    margin: 5px 7px 0 7px;
    width: 16px;
    height: 16px;
    vertical-align: middle;
}

.treegrid-ccmb-adv-item-drop-mark
{
    float: right;
}
.treegrid-ccmb-adv-item-drop-mark span
{
    background: url(app/integralui/resources/icons.png) -144px -80px no-repeat;
    display: inline-block;
    opacity: 0.5;
    overflow: hidden;
    padding: 0 !important;
    margin: 4px 2px 0 0;
    width: 16px;
    height: 16px;
    vertical-align: middle;
}
                            

Conclusion

By modifying the cell template in TreeGrid component for Angular, you can add a custom DropDown editor that will display checked item list. For this purpose, you can use the IntegralUIDropDown directive or any other Angular component with ability to show a popup window.

Based on settings in each individual cell, you can change how the dropdown list will appear. You can have a list of checked items first, followed by a list of sorted items. This helps in quickly navigating to specific option, if the list contains many items.

This example, only presents a DropDown editor. The TreeGrid component is fully customizable, and you can add any custom editor to any cell, or even use different editors for specific cell based on different conditions. There are no limitations, based on your application requirements; you can use any editor you need.

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.