We had a requirement in an AngularJS portlet to make jQuery DataTable columns dynamic. Normally, DataTable fetches a JSON object that contains the headers and uses that object to instantiate the DataTable. Doing it the Angular way is a bit different. The challenge is adhering to the way Angular does things where it is not advisable to put your service in a directive. Additionally, we want to instantiate the table only after we generate the headers using ng-repeat. Here is my go at it.
For the table we will be working on, the HTML looks like this:
<table id="datatable3" class="table table-striped table-hover" my-datatable aa-data="reportData">
<thead>
<tr>
<th ng-repeat="header in tableHeaders" generate-datatable>{{header.value}}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
In your AngularJS state, you can make it look something like this. There’s nothing special about this, other than I opted to use resolve to fetch the table data. That code can also be moved to the AngularJS service.
$stateProvider.state('table', {
url: '/table',
templateUrl: 'partial-table.html',
controller: 'angularDTController',
resolve: {
data: function($http){
return $http({method: 'GET', url: 'data.json'});
}
}
})
Create an Angular service that will fetch the headers. We will be injecting this service into the controller.
routerApp.factory('tableService', function($http) {
return {
getColumn: function() {
return $http({
method: 'GET',
url: 'columns.json'
});
}
}
});
In the controller, inject the data resolve and tableService.
routerApp.controller('angularDTController', function($scope, tableService, data) {
$scope.finishedHeader = false;
//need to get the columns
var promise = tableService.getColumn();r
promise.then(function(success) {
var data = success.data;
$scope.tableHeaders = data;
});
//this one sets the data to the datatable. The reportData is being watched in the directive
$scope.reportData = data.data.aaData;
});
Note that on line 2, $scope.finishedHeader = false; is what we will be using to tell the directive when to render the datatables, only after the header has been displayed using ng-repeat.
Lastly, create two directives, one for creating the table and one for informing the other that it has generated the last header and the datatable can be generated.
routerApp.directive('myDatatable', function(tableService) {
function link(scope, element, attrs) {
scope.$watch('finishedHeader', function(val) {
if (val === true) {
var t = $(element).dataTable({
sDom: '<"clear">TlfCrtip'
});
// watch for any changes to our data, rebuild the DataTable
scope.$watch(attrs.aaData, function(value) {
var val = value || null;
if (val) {
t.fnClearTable();
t.fnAddData(scope.$eval(attrs.aaData));
}
});
}
});
}
return {
link: link;
}
})
.directive('generateDatatable', function() {
function link(scope, element, attrs) {
if (scope.$last) {
scope.$parent.finishedHeader = true;
}
}
return {
link: link;
}
});
The directive generateDatable sets the finishedHeader scope variable to true when the last header is generated by the ng-repeat. This will trigger the $watch(‘finishHeader’) from the myDatatable directive to start generating the datatable.
You can checkout this plunker for a full demo.
The post Dynamic DataTables Headers Using AngularJS appeared first on Xtivia.