/**
* Custom AngularJS directive for fixed header scrollable tables
* @version 1.2.0
*/
(function () {
angular
.module('anguFixedHeaderTable', [])
.directive('fixedHeader', fixedHeader);
fixedHeader.$inject = ['$timeout'];
function fixedHeader($timeout) {
return {
restrict: 'A',
link: link
};
function link($scope, $elem, $attrs, $ctrl) {
var elem = $elem[0];
// Wait for data to load and then transform the table
$scope.$watch(tableDataLoaded, function(isTableDataLoaded) {
if (isTableDataLoaded) {
transformTable();
}
});
function tableDataLoaded() {
// The first cell in the tbody exists when data is loaded but doesn't have a width
// until after the table is transformed
var firstCell = elem.querySelector('tbody tr:first-child td:first-child');
return firstCell && !firstCell.style.width;
}
function transformTable() {
// Reset display styles so column widths are correct when measured below
angular.element(elem.querySelectorAll('thead, tbody, tfoot')).css('display', '');
// Wrap in $timeout to give table a chance to finish rendering
$timeout(function () {
// Set widths of columns
angular.forEach(elem.querySelectorAll('tr:first-child th'), function (thElem, i) {
var tdElems = elem.querySelector('tbody tr:first-child td:nth-child(' + (i + 1) + ')');
var tfElems = elem.querySelector('tfoot tr:first-child td:nth-child(' + (i + 1) + ')');
var columnWidth = tdElems ? tdElems.offsetWidth : thElem.offsetWidth;
if (tdElems) {
tdElems.style.width = columnWidth + 'px';
}
if (thElem) {
thElem.style.width = columnWidth + 'px';
}
if (tfElems) {
tfElems.style.width = columnWidth + 'px';
}
});
// Set CSS styles on thead and tbody
angular.element(elem.querySelectorAll('thead, tfoot')).css('display', 'block');
angular.element(elem.querySelectorAll('tbody')).css({
'display': 'block',
'height': $attrs.tableHeight || 'inherit',
'overflow': 'auto'
});
// Reduce width of last column by the width of the scrollbar
var tbody = elem.querySelector('tbody');
var scrollBarWidth = tbody.offsetWidth - tbody.clientWidth;
if (scrollBarWidth > 0) {
// For some reason, trimming the width by 2px lines everything up better
scrollBarWidth -= 2;
var lastColumn = elem.querySelector('tbody tr:first-child td:last-child');
lastColumn.style.width = (lastColumn.offsetWidth - scrollBarWidth) + 'px';
}
});
}
}
}
})();
var app = angular.module('myApp', ['anguFixedHeaderTable'])
.controller('DemoController', function($scope) {
$scope.products = [
{
displayName: 'Prod1',
marketValue: '100',
positions:'1'
},
...
// Additional product objects here
...
{
displayName: 'Prod1',
marketValue: '100',
positions:'1'
},
];
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="DemoController">
<table table-height="100px" class="table table-bordered table-striped table-hover" fixed-header>
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
<th>Id</th>
</tr>
</thead>
<tbody>
<tr class="info" ng-repeat="item in products track by $index">
<td>
<input type="checkbox"/>
<a>{{item.displayName}}</a>
</td>
<td>
{{item.marketValue}}
</td>
<td>
{{item.positions}}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Name</td>
<td>Amount</td>
<td>Id</td>
</tr>
</tfoot>
</table>
</body>
</html>