a suite of UI Components for development of web apps
Advanced User Interface Controls and Components
Created: 09 October 2015
Column header in AngularJS Grid directive by default only shows a text in one line. In order to add custom content in column header we can use a template. In case of a dropdown menu for grid columns, we can add a button to column header which when clicked will display a predefined menu.
Similar: Add DropDown Button to Column Header in Angular Grid
As above demonstration shows, each column has a menu button. When this button is clicked, it will display a dropdown menu with specific options. The following sections below, shows how to create this in AngularJS.
Because the grid column by default only displays a text, in order to add a menu button, we need to create a template. This template is simple, only contains an element for column title and an element for menu button.
As a menu we will use the IntegralUI ContextMenu directive which is part of IntegralUI Studio for Web, that allow us to create a multi-level menu and apply it to any other directive or HTML element. In our case, it is applied to the menu button.
<div ng-app="appModule" ng-controller="appCtrl">
<script type="text/ng-template" id="column-menu.html">
<div class="column-header">
<span>{{obj.text}}</span>
<span class="column-menu-button" iui-contextmenu="obj.menu"></span>
</div>
</script>
.column-header
{
margin: 0;
padding: 0 3px;
}
.column-menu-button
{
background-image: url(resources/icons.png);
background-position: -176px -80px;
background-repeat: no-repeat;
border: thin solid #bebebe;
cursor: default;
display: inline-block;
position: absolute;
top: 5px;
right: 3px;
margin: 0;
padding: 0;
width: 16px;
height: 16px;
}
.column-header:hover > .column-menu-button
{
background-position: -192px -80px;
border-color: white;
}
We are creating this template in our page so that it is added to the template cache, so that AngularJS quickly retrieve it. Because we want to use this template for all grid columns, while still setting a different referencing object for each column, the best way is to create a custom directive.
angular
.module("appModule", ["integralui"])
.directive(, function() {
return {
restrict: 'E',
templateUrl: 'column-menu.html',
scope: {
obj: '='
}
};
})
.controller("appCtrl", ["$scope", "IntegralUITreeGridService", "$timeout", function($scope, $gridService, $timeout){
// A unique identifier of the Grid directive
$scope.gridName = "gridSample";
// An array object that holds the grid columns
$scope.columns = [];
// An array object that holds the grid rows
$scope.rows = [];
// An object that holds the column with active dropdown menu
var activeColumn = null;
}]);
<div ng-app="appModule" ng-controller="appCtrl">
<iui-treegrid name="{{gridName}}" columns="columns" rows="rows" show-footer="false" allow-focus="false" update-complete="onUpdateComplete()"></iui-treegrid>
</div
The structure of this directive is simple, only shows that is uses our template and contains only public property which will hold the referencing object.
Now we can create our columns. We will use the headerContent field of column object to link the column with the template. Therefore, column header will now display a HTML content generated using the template as a replacement default column header text.
// Objects passed to the column template
$scope.header1 = { text: "Header 1", menu: $scope.menu }
$scope.header2 = { text: "Header 2", menu: $scope.menu }
$scope.header3 = { text: "Header 3", menu: $scope.menu }
// An set of columns displayed in the grid
// Each column has custom content with its own dropdown menu
$scope.columns = [
{
id: 1,
headerObj: $scope.header1,
headerContent: '<custom-header obj="header1"></custom-header>',
width: 230
},
{
id: 2,
headerObj: $scope.header2,
headerContent: '<custom-header obj="header2"></custom-header>',
width: 180
},
{
id: 3,
headerObj: $scope.header3,
headerContent: '<custom-header obj="header3"></custom-header>',
width: 150
}
];
As it can be seen from code above, we are using the custom directive (called customHeader), with different referencing object for each grid column.
Our next step is to create the menu structure and apply it to the column using the referencing object. As our menu items we will set several items which when clicked will reorder grid columns, and one submenu item which holds the visibility status of all columns.
// Data structure for the dropdown menu
$scope.menu = {
activate: 'both',
itemIcon: 'icons empty',
items: [
{ text: 'Move First', key: 'MOVE_FIRST' },
{ text: 'Move Left', key: 'MOVE_LEFT' },
{ text: 'Move Right', key: 'MOVE_RIGHT' },
{ text: 'Move Last', key: 'MOVE_LAST' },
{ type: 'separator' },
showColumnsMenu
],
position: 'below'
}
The menu item that holds the submenu showing column visibility status is created and updated whenever column visibility changes. For this purpose, we are handling the updateComplete event, so that menu is updated.
// Retrieves an icon for the menu item
var getMenuIcon = function(column){
return column.visible != false ? 'icons check-mark' : null;
}
// Retrieves a list of columns shown as options in submenu
var getColumnList = function(){
var list = [];
for (var j = 0; j < $scope.columns.length; j++){
list.push({
icon: getMenuIcon($scope.columns[j]),
text: $scope.columns[j].headerObj.text
});
}
return list;
}
// An object that holds the submenu
var showColumnsMenu = {
text: 'Show Columns',
items: getColumnList()
}
// An handler for the updateComplete event, fired when grid layout is updated
$scope.onUpdateComplete = function(){
showColumnsMenu.items = getColumnList();
}
Now we have our menu created, but still it does nothing. In order to set an action whenever a menu item is clicked, we need to handle the itemClick event:
// Data structure for the dropdown menu
// Handlers for itemClick and open events
$scope.menu = {
activate: 'both',
itemIcon: 'icons empty',
items: [
{ text: 'Move First', key: 'MOVE_FIRST' },
{ text: 'Move Left', key: 'MOVE_LEFT' },
{ text: 'Move Right', key: 'MOVE_RIGHT' },
{ text: 'Move Last', key: 'MOVE_LAST' },
{ type: 'separator' },
showColumnsMenu
],
position: 'below',
itemClick: function(e){
if (e.item){
$gridService.suspendLayout($scope.gridName);
// Add Menu functionality here
$gridService.resumeLayout($scope.gridName);
}
activeColumn = null;
},
open: function(e){
activeColumn = $gridService.getHoverColumn($scope.gridName);
}
}
Each menu item has a different key, as unique identifier. We will use this key value to determine which item is clicked and then proceed with a different operation.
Also, because all columns are using the same template, we need to know from which grid column the dropdown menu is shown. For this purpose, we are handling the open event. Whenever the dropdown menu is displayed, we are setting a local object that holds the referencing column. The column is retrieved using the getHoverColumn method.
Now we have a functional menu with several different menu options that will perform a different action. We have two groups of operations: moving of columns and changing their visibility.
We can move a column in the grid to appear as first or last column, or we can move it by one-step to the left or right. This is done by using the move method, which accepts as parameters the reordering column and the direction in which we want to move it.
// Code part from itemClick event, functionality of moving columns
switch (e.item.key){
case 'MOVE_FIRST':
if (activeColumn)
$gridService.moveColumn($scope.gridName, activeColumn, 'first');
break;
case 'MOVE_LEFT':
if (activeColumn)
$gridService.moveColumn($scope.gridName, activeColumn, 'left');
break;
case 'MOVE_RIGHT':
if (activeColumn)
$gridService.moveColumn($scope.gridName, activeColumn, 'right');
break;
case 'MOVE_LAST':
if (activeColumn)
$gridService.moveColumn($scope.gridName, activeColumn, 'last');
break;
}
As for changing column visibility, we simple change the value of visible field of column object. Whenever this value is changed, the column becomes hidden or visible, and the menu is updated to reflect that change.
// Code part from itemClick event, functionality of grid column visibility changes
var index = showColumnsMenu.items.indexOf(e.item);
if (index >= 0 && index < $scope.columns.length){
$scope.columns[index].visible = $scope.columns[index].visible == undefined ? false : true;
e.item.icon = getMenuIcon($scope.columns[index]);
}
We have created a Grid where each column header is created using a custom template consisting of a title and a button. Whenever a button is clicked, a dropdown menu is displayed with several options with different operations that will either move the specified column or change its visibility status.
The dropdown menu can contain any number of different options set in multiple levels. Depending on your application requirements, you can create a different data structure for the menu. It is fully customizable, in both appearance and functionality.