Angular

How to Use a JavaScript DataTable in an Angular Application

Fork me on GitHubThere are several data table widgets for Angular, but none of them matched our project’s needs. I’m sure that’s simply a matter of time. Angular2 is young, and the third-party libraries are even younger. They simply didn’t have enough time to accumulate features and maturity. So why don’t we use one of the seasoned JavaScript data tables?

As it turns out, Louis Lin had the same idea creating the Angular DataTables project. It looks promising, but at the time of writing, it was only 26 days old. So the feature list is pretty short. When you’re reading this article, things have probably improved. However, today I don’t want to tell you how to use a third-party data table. Instead, I’ll tell you how to do it yourself. Before we start to wallow in the source code, let’s start with what developers usually do: a short market survey. If you’re in a hurry, skip that section or jump directly to the source code of the demo on GitHub.

Short Angular data table market survey (as of Jan 21, 2016)

I’ve already mentioned Louis Lin’s project. He does exactly what we’re doing later in this article: integrating the table from DataTables.net. The demos look nice, and I guess you can pass almost every option of the JavaScript widget to the angular-datatables. If my guess is correct, compensates the lack of features the demos show.

“Lack of features” is not the word coming to mind looking at the demo of Swimlane’s ngx-datatable. The only reason we didn’t use it was because our project uses Bootstrap. ngx-datatable supports theming, so it’s possible to provide a Bootstrap theme yourself. Maybe we’ll do that later.

The ng2-table of valor-software is great because it’s extremely simple. You provide a column description, pass data to the table and that’s it. We used this table in our project for a couple of weeks. However, we needed editable cells, and we needed to select rows, so this table was too simple for us.

Another interesting table is the ng2-smart-table. Unfortunately, the fast pace of Angular updates breaks the project every once in a while. We didn’t use it simply because it wasn’t compatible with our version of Angular when we evaluated the table. Keep in mind that this is probably only a matter of time. Chances are the bug has been fixed when you read this article.

The grids of Kendo and Wijmo are almost certainly great choices, too. Too bad they are not available for free. So we chose to evaluate the other choices.

The documentation of the ngx-datatable lists several other alternatives. I didn’t examine them, so suffice it to mention them: ng2-super-table, vaadin-grid, angular2-iron-data-table (which is built on a Polymer datatable) and another Material Design table, the paper-datatable.

Do it yourself (but don’t do it from scratch)

The next option is to use one of the existing data tables and integrate them in an Angular application. I chose the table from DataTables.net, which is build on jQuery. That’s a fine library we also use in the BootsFaces project. I could have chosen Chinese data table just as well. But I’m more familiar with DataTables.net.

There are several challenges to overcome: for instance, the Angular life-cycle isn’t exactly compatible with the jQuery events. Adding insult to injury, the data table is available on NPM, but it seems to support Common.js, while the Angular CLI uses another module system.

By the way, if you don’t use the Angular CLI, I recommend the article of
Mitch Talmadge
, who integrates DataTables.net using Webpack.

Adding the dependencies to the package.json

Back to the Angular CLI approach. The first step is to install the dependencies:

npm install bootstrap --save
npm install datatables.net --save
npm install datatables.net-bs --save
npm install datatables.net-select --save
npm install datatables.net-select-bs --save
npm install jquery --save
npm install @types/jquery --save-dev

It’s also a good idea to add types for the DataTable widget. As far as I can see, there are several type definition files. But there’s no official type definition which is regularly maintained. This article simply casts the type to any. That’s far from ideal, but it works. If you know a good type definition file, please leave a comment so I can improve this article.

Adding the files to the angular-cli.json

Next we add the CSS files to the angular-cli.json file:

     "styles": [
        "styles.css",
        "../node_modules/bootstrap/dist/css/bootstrap.min.css",
        "../node_modules/bootstrap/dist/css/bootstrap-theme.min.css",
        "../node_modules/datatables.net-bs/css/dataTables.bootstrap.css",
        "../node_modules/datatables.net-select-bs/css/select.bootstrap.css"
      ],

Creating the component

Now it’s time to create the component for the data table using the command line command "ng g c datatable".

The HTML template file of the component is straight-forward, pretty much the way it’s documented on the DataTables.net page:

   <table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
      <thead>
      <tr>
        <th>First name</th>
        <th>Last name</th>
      </tr>
      </thead>
       <tbody>
      <tr *ngFor="let row of data; let i = index">
        <td>{{row.name}}</td>
        <td><input [(ngModel)]="row.lastName" style="color:black"></td>
      </tr>
      </tbody>
    </table>

This generates a table with a static cell and input fields in the second column.

TypeScript imports

The component class imports both jQuery and the Datatable like so:

import * as $ from 'jquery';
import 'datatables.net'

The second import statement circumvents the module system of TypeScript. It simply registers the DataTable as a jQuery plugin, which is fine by us.

Initializing the DataTables.net widget

The component initializes the data table in the ngAfterViewInit method. This relieves us from having to write an onDocumentLoad handler we’d use without Angular. Not a big deal, but still, it improves readability.

export class DatatableComponent implements OnInit {
 public tableWidget: any;

 ngAfterViewInit() {
    this.initDatatable()
  }

  private initDatatable(): void {
    let exampleId: any = $('#example');
    this.tableWidget = exampleId.DataTable({
      select: true
    });
  }
  ...
}

Adding and deleting rows

At first glance, the table already looks great. It’s even possible to add or remove array elements, and the data table is redrawn. But there’s a catch: sorting and filtering ignores the new rows.

So adding and deleting rows requires us to redraw the table. If you’ve got a simple table without input fields, it’s possible to use the data object of the data table to add or delete rows. Unfortunately, the input field prevents that. I’m sure there’s a better way, but I solved the problem by a brute-force approach: destroying and re-initializing the table does the trick. The disadvantage is that the table flickers. If you know how to do it better, please leave a comment. Until then, I’ll show you the brute force approach:

 private reInitDatatable(): void {
    if (this.tableWidget) {
      this.tableWidget.destroy()
      this.tableWidget=null
    }
    setTimeout(() => this.initDatatable(),0)
  }

  public deleteRow(): void {
    this.data.pop();
    this.reInitDatatable()
  }

The timeout is needed to make sure that the data table is initialized after Angular has redrawn the page. Otherwise, you may end up with the message “no data” under a long table.

jQuery

Remains the question how to deal with jQuery events. By default, modifying attributes in a jQuery event listener doesn’t trigger Angular’s change detection. We can fix this using an event emitter:

  @Output() rowSelected: EventEmitter<number> = new EventEmitter();

  private initDatatable(): void {
    let exampleId: any = $('#example');
    this.tableWidget = exampleId.DataTable({
      select: true
    });
    this.tableWidget.on('select',
      (e, dt, type, indexes) => this.onRowSelect(indexes))
  }
  
  private onRowSelect(indexes: number[]): void {
    this.rowSelected.emit(indexes[0])
  }

For some reason, during my tests, the array indexes always contained a single number, no matter how many rows I’d selected. That’s why the event emitter only broadcasts a single number instead of the entire array.

Using the component

After these preparations, using the data table is straight-forward:

<datatable (rowSelected)="onRowSelected($event)"></datatable>
export class AppComponent {

  public onRowSelected(index: number) {
    console.log(index)
  }
}

Wrapping it up

This article has become a bit long, which may give you the impression integration a JavaScript data table library in an Angular application is difficult. In reality, it’s pretty simple. The solution I presented is far from ideal, but it’s good enough for most use cases, and it took me merely a couple of hours to implement it. Truth to tell, writing this article took more time than writing the actual code.

The extra value the full-blown Angular2 libraries give you is basically syntactical sugar (which is great!) and a better integration with the Angular life cycle. The trade-off is that these libraries are usually opinionated in one way or another.

Dig deeper

Source code of the demo on GitHub

18 thoughts on “How to Use a JavaScript DataTable in an Angular Application

  1. I have followed the instruction…
    In my case, datatables plugin is loaded, but If I try to filter or to change sort, all the data disappear and no error are logged… =(

    1. Oh yes, I remember this bug. The problem is that the datatable isn’t initialized correctly. What I do not remember is how we fixed the bug. It can be fixed, that’s for sure. Basically, you have to options: delay the initialization of the datatable until Angular has rendered the HTML code, and add the data to the data (?) array.

      In theory, I could look up what we did to fix the bug, but in practice, that’s difficult because we’ve replaced Datatables.net by the datatable of PrimeNG. That, in turn, is a widget I highly recommend. It works like charm.

      What do we learn from all this? Well, it’s possible to use an old and proven jQueryUI widget in an Angular application. But it’s always a compromise. Try to replace it with a native implementation whenever possible.

      1. I too facing the same issue. All data disappears when you do sort/search/ any operations…

        I tried ngafterviewinit, but could not make it work. I just populated the array and tried, but no luck 🙁

        Can you help me with the fix please ?

        1. I solved the issue by letting the DataTable build the table through the data property. In my case, I get the data with a webservice and then construct the datatable.

    1. Which buttons do you refer to? Would you mind to explain your problem in a couple of sentences?

      1. I want to use Export buttons especially, like Excel, PDF, HTML5, etc. And I don’t know how to use them. I tried to include related CSS (after getting npm packages), and importing “datatables.net-buttons” inside the Component.

        But there is no button displaying, and I am using “Btip” as DOM for datatables.

  2. I’ve followed this article in aurelia and typescript project, but still get an error: DataTable is not a function or property,

  3. I carried out the steps and the datatable is working. i am looking for ways to customise the component?

    For example i want to add edit-delete to the first column. Also move the show entries near the paging buttons.

    Also add a first and previous to the previous/next buttons.

    which of the NPM build steps i have to repeat again? all five of them and recreate the component again?

    1. That’s a lot of question, but none of them require NPM.

      Basically, adding an edit or delete button simply means you render the HTML code of such a button in the first column, and attach it with a add() or delete() method of your datatable component. Updating the component is a bit of a challenge, but I’ve already covered that in the article above.

      As for the other questions, I recommend digging into the huge documentation of datatables.net. I have to admit it often takes a while to find what you’re looking for. But this particular datatable is incredibly flexible, so I’m positive it covers most of your requirements. Like I’ve mentioned before, the most annoying challenge is to align the lifecycle of jQueryUI and the datatable with the lifecycle of Angular.

      Maybe even the BootsFaces library helps you. The page I’ve linked is “only” the documentation – but using the debugger of your browser you can reverse-engineer what you’re looking for. Of course, there’s also the source code of BootsFaces itself, but that’s possibly the more stony ways.

Leave a Reply

Your email address will not be published.