0

I'm currently writing an application using Angular 2 and have it configured to use DataTables.net, DataTables.net-bs, and DataTables.net-select.

For the most part, everything looks and works great. With one exception, the pagination for the application shows up with as though no styling is applied.

I checked the source within the browser and the default classes and HTML structure is applied:

<div class="dataTables_paginate paging_full_numbers" id="myTable_paginate">
    <a tabindex="0" class="paginate_button first disabled" id="myTable_first" aria-controls="myTable" data-dt-idx="0">First</a>
    <a tabindex="0" class="paginate_button previous disabled" id="myTable_previous" aria-controls="myTable" data-dt-idx="1">Previous</a>
    <span>
        <a tabindex="0" class="paginate_button current" aria-controls="myTable" data-dt-idx="2">1</a>
        <a tabindex="0" class="paginate_button " aria-controls="myTable" data-dt-idx="3">2</a>
        <a tabindex="0" class="paginate_button " aria-controls="myTable" data-dt-idx="4">3</a>
        <a tabindex="0" class="paginate_button " aria-controls="myTable" data-dt-idx="5">4</a>
        <a tabindex="0" class="paginate_button " aria-controls="myTable" data-dt-idx="6">5</a>
        <span class="ellipsis">…</span>
        <a tabindex="0" class="paginate_button " aria-controls="myTable" data-dt-idx="7">580</a>
    </span>
    <a tabindex="0" class="paginate_button next" id="myTable_next" aria-controls="myTable" data-dt-idx="8">Next</a>
    <a tabindex="0" class="paginate_button last" id="myTable_last" aria-controls="myTable" data-dt-idx="9">Last</a>
</div>

The functionality of the links work and the it just looks ugly on the display because I'm using the DataTables Bootstrap CSS and it is not outputting the appropriate HTML using an unordered list. I went into the debugger in the browser and the code for the DataTables.net-bs is being loaded. I added a bunch of break points to the JavaScript and the factory method is being called. However, it appears that it is never called again.

Here is the relevant code for my vendors.browser.ts

require('datatables.net')();
require('datatables.net-bs')();
require('datatables.net-select')();
require('file-saver');

I also have AMD turned off because I know that was an issue for some people. Here is the relevant code for the webpack.common.js file:

    {
      test: /datatables\.net.*/,
      loader: 'imports?define=>false'
    },

As an aside, the other plugin, Datatables.net-select, functions properly.

If I can't figure this out, I can use the styling included in DataTables.net-dt, but I would much rather get this working properly.

Does anyone have any potential ideas on what is going on?

2 Answers 2

4

STEPS TO INTEGRATE datatables.net TO ANGULAR 4 CLI

We need to install datatables.net as a global library (available to all components). This means that the library will not be lazy-loaded, but rather imported/downloaded on the first page load.

After 2 days of research and a lot of digging into Angular CLI I was able to make it work with the latest version of the CLI (1.1.3) and Angular 4

These are the relevant articles in the wiki that helped me:

https://github.com/angular/angular-cli/wiki/stories-global-scripts

https://github.com/angular/angular-cli/wiki/stories-third-party-lib

  1. Install JQuery and its typings

    npm install --save jquery
    npm install --save-dev @types/jquery
    
  2. Install datatables.net and its typings

    npm install --save datatables.net
    npm install --save-dev @types/datatables.net
    
  3. [OPTIONAL] Add datatables.net theme (bootstrap theme)

    npm install --save datatables.net-bs
    
  4. [OPTIONAL] Add datatables.net extensions (select, buttons, etc.)

    npm install --save datatables.net-select
    npm install --save datatables.net-select-bs
    npm install --save-dev @types/datatables.net-select
    
  5. Now lets configure typyings so we let the compiler understand these libraries and don't error while compiling. Open tsconfig.app.json and under types node add the following typings:

    "types": [
        "YOUR_OTHER_TYPINGS_HERE",
        "jquery",
        "datatables.net",
        "datatables.net-select"
    ]
    
  6. Finally, we need to add our libraries to the global scripts and styles section. Open tsconfig.app.json file and update the styles and scripts sections as follows:

    "styles": [
        "YOUR_OTHER_CSS_HERE",
        "../node_modules/bootstrap/dist/css/bootstrap.css",
        "../node_modules/datatables.net-bs/css/dataTables.bootstrap.css",
        "../node_modules/datatables.net-select-bs/css/select.bootstrap.css",
        "YOUR_OTHER_CSS_HERE"
    ],
    "scripts": [
        "YOUR_OTHER_JS_LIBS_HERE",
        "../node_modules/jquery/dist/jquery.js",
        "../node_modules/bootstrap/dist/js/bootstrap.js",
        "../node_modules/datatables.net",
        "../node_modules/datatables.net-bs/js/dataTables.bootstrap.js",
        "../node_modules/datatables.net-select/js/dataTables.select.js",
        "YOUR_OTHER_JS_LIBS_HERE",
    ],
    

    Just make sure you set the order of the libraries right, it doesn't really matter if you add libraries in the middle. But essentially you need JQuery, then Bootstrap, then datatables.net, and finally any datatables.net extensions or plugins you may need.

Please note that the same procedure should be done for importing almost any other JQuery plugin to an Angular 4 CLI project.

Now you can use datatables in a component as follows:

import { Component, OnInit, Input, Output, EventEmitter, ElementRef } from '@angular/core';
import { Shipment } from '../../models';

@Component({
    selector: 'shipment-list',
    template: `
        <table id="shipments_table" class="table table-striped table-bordered table-hover no-footer">
        </table>
    `,
    styleUrls: ['./shipment-list.component.css']
})
export class ShipmentListComponent implements OnInit {
    private shipmentsTable: any;
    private tableWidget: any;
    @Input() shipments: Shipment[];
    // Event Emmiter for when the user selects a row
    @Output() shipmentSelected: EventEmitter<Shipment> = new EventEmitter();
    constructor(
        private el: ElementRef // You need this in order to be able to "grab" the table element form the DOM 
    ) { }

    public ngOnInit() {
        this.loadShipments();
    }
    public loadShipments(): void {
        if (this.tableWidget) {
            this.tableWidget.destroy(); // essentially refreshes the table
            // you can also remove all rows and add new
            // this.tableWidget.clear().rows.add(this.shipments).draw();
        }
        let tableOptions: any = {
            data: this.shipments,
            dom: 'rt',
            select: true,
            columns: [
                { title: 'Content', data: 'content' },
                { title: 'Packages', data: 'packages' },
                { title: 'Weight', data: 'weight' }
            ]
        }
        this.shipmentsTable = $(this.el.nativeElement.querySelector('table'));
        this.tableWidget = this.shipmentsTable.DataTable(tableOptions);
        this.tableWidget.on('select', (e, dt, type, indexes) => {
            // I DIDN'T TRY THIS IN HERE...Just debug it and check the best way to emit an actual object
            this.shipmentSelected.emit(this.shipments[indexes[0]]);
        });
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

thanks for the info, but there is an error in 6th step: "../node_modules/datatables.net", should be: "../node_modules/datatables.net/js/jquery.dataTables.js",
1

I resolved the problem.

The issue was with this code:

require('datatables.net')();
require('datatables.net-bs')();
require('datatables.net-select')();
require('file-saver');

The problem is within the checks at the beginning of the modules for DataTables.net. At the top of each module, you have the following:

(function( factory ){
    if ( typeof define === 'function' && define.amd ) {
        // AMD
        define( ['jquery', 'datatables.net'], function ( $ ) {
            return factory( $, window, document );
        } );
    }
    else if ( typeof exports === 'object' ) {
        // CommonJS
        module.exports = function (root, $) {
            if ( ! root ) {
                root = window;
            }

            if ( ! $ || ! $.fn.dataTable ) {
                // Require DataTables, which attaches to jQuery, including
                // jQuery if needed and have a $ property so we can access     the
                // jQuery object that is used
                $ = require('datatables.net')(root, $).$;
            }

            return factory( $, root, root.document );
        };
    }
    else {
        // Browser
        factory( jQuery, window, document );
    }

With the above code, we are not passing in the global $ variable. When the first import runs, the $ variable is null and so it sets the value appropriately. When the second import, the value passed in is null and it gets reset along with the extensions for the plugin. When the third import runs, we have the same issue. The select functionality is now there, but we overwrote the functionality for the Bootstrap plugin.

So, the corrected code is the following:

require('datatables.net')(window, $);
require('datatables.net-bs')(window, $);
require('datatables.net-select')(window, $);
require('file-saver');

I was basing my code on this: [https://github.com/brakmic/Angular2-Articles/blob/master/article6/src/init/vendor.ts][1]

window.$ = window.jQuery = require('jquery');
require('bootstrap-loader');
require('datatables.net')();
require('datatables.net-bs')();
require('datatables.net-buttons')();

My typescript editor was barfing at the first line, so I removed it not fully understanding what was going on behind the scenes. Lesson learned.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.