Table with fixed header?


Has anyone ever created a data table / ActiveForm with Canvas with a fixed table header, i.e. scrolling down will leave the header where it is - like we all did in good old ActiveForms in Perspectives.
I assume this topic is not really related to Canvas itself, but I would be very interested to see a working example with Canvas. Would also be good to have something like that as built-in attribute in Canvas, as I expect there are a lot of needs for this scenario.

Thanks for any hint.




I’ve set up freeze pane tables for a client.

You need some additional JavaScript and css.

I will create a page in the /samples instance and send you an example on Thursday.



Jack, that would be awsome. Basically the only piece of puzzle I’m still missing for a painless transition from Websheets to Canvas. I had some tries with different CSS from different sources, but they all messed up my table layout, so can’t wait to see a working example. Thank you very much!


Hey Andreas,

Please see the below, you’ll need to create a new blank canvas page called “exaple freeze panes”, and then replace the html and js files with the below. (in samples)

You can either replace the css file with the below, or append the content to your own.


example freeze panes.html (8.2 KB)

example freeze panes.js (1.6 KB)

style.css (792 Bytes)

Column Freezing

Hi @jtuckerman,

Thanks for sharing but unfortunately we are not able to download the .js file. Could you please reupload your file but add .txt extension so we can download it.



The forum won’t let me upload .txt files, so here’s what needs adding to the controller:

app.controller('examplefreezepanesCtrl', ['$scope', '$rootScope', '$log', '$http', '$tm1UiTable', '$tm1Ui', function($scope, $rootScope, $log, $http, $tm1UiTable, $tm1Ui) {

  $ = {accounts: []};

  $scope.filter = function(value){

    if($ && $ != ""){
      $ $;
      if(value["Description"].toLowerCase().indexOf($ == -1){
        return false;

    return true;

  $scope.table = $tm1UiTable.create($, {pageSize: 30, preload: false, filter: $scope.filter});

  fixedTable = function(el) {
		var $body, $header, $sidebar;
		$body = $(el).find('.fixedTable-body');
		$sidebar = $(el).find('.fixedTable-sidebar table');
		$header = $(el).find('.fixedTable-header table');
		return $($body).scroll(function() {
			$($sidebar).css('margin-top', -$($body).scrollTop());
			return $($header).css('margin-left', -$($body).scrollLeft());

	tablescroll = new fixedTable($('#tablescroll'));

  $scope.firstPage = function(){
    var recordsPerPage = $scope.table._pageSize;
    $scope.table = $tm1UiTable.create($, {index: 0, pageSize: recordsPerPage, preload: false, filter: $scope.filter});

  $scope.lastPage = function(){
    var totalRecords = $scope.table._pages;
    var recordsPerPage = $scope.table._pageSize;
    var newIndex = Math.floor(totalRecords - recordsPerPage);
    $scope.table = $tm1UiTable.create($, {index: newIndex, pageSize: recordsPerPage, preload: false, filter: $scope.filter});



It worked like a charm:

Thanks Jack


Jack, this example is brilliant and works perfectly following your instructions, as Vincent already commented.
Will now try to transfer this to a working example in our company.

PS: I did not expect fixed columns to work the same way, so even better. At the end I would need both as our finance guys prefer to stare at huge tables … :smirk:

Thanks a lot,



Thanks Jake for that, it works great!

I’ve got a couple of questions:
The side rows (or rows’ headers) and the main table’s rows would become misaligned:

  • if the label is too long and appears on 2 or 3 lines, increasing the row’s height while the rows of the main body keep their original height
  • if there are cells you can input in the main body, increasing the height of the row while the label keeps its original height.
    => To fix that I’ve fixed the table’s rows height to 60px (best compromise I could find)

.fixedTable .table tr {
height: 60px !important;

Did you guys have any other idea / a more elegant way to address that?

And other thing, did the sorting by clicking on the column’s header kept working after spliting the table in 4?
(On my side it seems doing a really random sorting)



Hey Celine,

With the table broken into 4 divs fixed width/heights are your only options.

UI-grid (installed in Canvas by default) supports column/header fixing, but you can’t have stacked columns.

And, no, we’ve had no issues with the sorting.