Datatable with fully dynamic columns in angularJS

First: there is a plunkr example for the following code examples.

While doing some testing for a possible new project which we want to do in angularJS I discovered that its quite hard to implement a datatable which should have fully dynamic columns which can be reordered and toggled to display or not.

Many datatable implementations have either hard-coded columns which you can’t change at all or you can’t change the order of the columns in which they should be rendered. ngTable does it like that for instance.

So, I spent a rainy sunday afternoon to implement my own version and after much poking around and some frustrating moments, I finally got it.

The first hurdle I had to take was, that angularJS can’t handle directives that have a <tr> or <td> element as the root element of the directive. There is an issue for this where its explained that these elements need to be handled special, which jQuery does, but jqLite in angularJS does not. There is a pull request for this too, but its not accepted yet.

After discovering that, I wrote a little helper function to create the elements like this:

function createTDElement() {
	var table = angular.element('<table><tr><td></td></tr></table>');
	return table.find('td');
}

The next problem I had was to implement the full dynamic columns which should be customizable from an angular controller. I didn’t want just define a hard-coded table with some columns in a html file and manipulate the DOM elements after they were created by angularJS. I wanted to do it the angular way.

I decided that every column needs to have its own directive where the output inside the cells is defined. These directives are not known to the wrapper directive, but are part of the customization inside the controller, so the wrapper is fully generalized and can be used with any type and any number of columns. The wrapper directive adds additional elements into the DOM in the $compile phase of angularJS which have the correct column directives set to them dynamically.

The html in the view looks like this:

<table border="1">
    <tr ng-repeat="item in items" item="item" columns="columns">
</table>

Inside the controller you have something like this:

function myController($scope) {
   $scope.items = [//someitems];

  $scope.columns = [{
    id: "column2",
    title: "Manufacturer",
    directive: "secondcolumn",
    visible: true
  }, {
    id: "column1",
    title: "ID",
    directive: "firstcolumn",
    visible: true
  }, {
    id: "column3",
    title: "Country",
    directive: "thirdcolumn",
    visible: false
  }, ];
}

So in this case the table should render with 2 columns: column2 first, then column1 and column3 should not be visible. The property “columnDirective” is used to find out the directive that renders this columns html output.

$scope.items is the data object, that you want to display in the table.

The important parts of the column directive, which does all the magic, look like this:

function createTDElement(directive) {
	var table = angular.element('<table><tr><td ' + directive + '></td></tr></table>');
	return table.find('td');
}
app.directive('item', function($compile) {
  function createTDElement(directive) {
    var table = angular.element('<table><tr><td ' + directive + '></td></tr></table>');
    return table.find('td');
  }

  function render(element, scope) {
    var column, html, i;
    for (i = 0; i < scope.columns.length; i++) {
      column = scope.columns[i];
      if (column.visible) {
        html = $compile(createTDElement(column.directive))(scope);
        element.append(html);
      }
    }

  }

  return {
    restrict: 'A',
    scope: {
      item: "=",
      columns: "="
    },
    controller: function($scope, $element) {
      $scope.$watch(function() {
        return $scope.columns;
      }, function(newvalue, oldvalue) {
        if (newvalue !== oldvalue) {
          $element.children().remove();
          render($element, $scope);
          $compile($element.contents())($scope);
        }
      }, true);
    },
    compile: function() {
      return function(scope, element) {
        render(element, scope);
      }

    }
  };

});

(The implementations for the columns are missing here).

And thats it.

I created a plunkr which has an working example for this so you can play around with it.

Tell me if you know some way to implement this easier.

7 thoughts on “Datatable with fully dynamic columns in angularJS

  1. WE were struggling hard to figure out a way how to add dynamic column to the datatable and you just came out for the rescue…may you have more such “rainy sunday afternoon” 😉 Thanks

  2. HI,
    Thanks lot for this wonderful stuff.
    I have problem now that the number of column is dynamic and I need to achieve the same column hide and show similar to this.. Is there a way to do this.

Leave a Reply to Somdev Bhattacharya Cancel reply

Your email address will not be published. Required fields are marked *

*