Angular 2, DataTables, and Pagination using DataTables.net-bs

Angular 2, DataTables, and Pagination using DataTables.net-bs

hirntotfurimmerhirntotfurimmer Posts: 6Questions: 1Answers: 0

Hello,

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?

This question has an accepted answers - jump to answer

Answers

  • hirntotfurimmerhirntotfurimmer Posts: 6Questions: 1Answers: 0
    edited April 2017

    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 (edited by allan per comment below):

    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.

  • jessemoon0jessemoon0 Posts: 3Questions: 1Answers: 0

    Hi, do you have a tutorial on this? Im trying to adapt data tables.net to angular 4... I would appreciate a lot!

    Thanks in advance
    Jessie

  • hirntotfurimmerhirntotfurimmer Posts: 6Questions: 1Answers: 0

    Sorry for the late reply. I didn't see that anyone responded to this thread.

    I don't have a tutorial. However, this series of tutorials was very helpful: blog.brakmic.com/category/coding/javascript/angular/.

    What was also most helpful to me was this repository by the author of the tutorials:

    https://github.com/brakmic/Angular2-Articles/tree/master/article6/src

  • allanallan Posts: 63,689Questions: 1Answers: 10,500 Site admin

    Thanks for posting back and updating your response above with the information about passing the jQuery object in!

    I need to get around to writing such tutorials myself! The one thing that keeps me from doing that is that I've never found a nice way of mixing Require and CSS. Are you using a solution for that?

    Allan

  • hirntotfurimmerhirntotfurimmer Posts: 6Questions: 1Answers: 0
    edited April 2017

    Also, if you happen to read this again, Allan, is there a way to edit the responses? For some reason, I cannot edit the first response.

    The code in that response:

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

    Should be:

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

    The application, through WebPack, the global variables window and $ defined so you can pass them into the require function. If it is not possible, I'll leave that here for anyone else who happens upon this thread.

  • hirntotfurimmerhirntotfurimmer Posts: 6Questions: 1Answers: 0

    For some reason, the forum ate my other response to you.

    I didn't do anything special for including the CSS. I am still new to Angular 2 and I chose a template that used WebPack.

    In the vendor.browser.ts section, I included the following code:

    require('style-loader!datatables.net-bs/css/dataTables.bootstrap.css');
    require('style-loader!datatables.net-fixedheader-bs/css/fixedHeader.bootstrap.css');
    require('style-loader!datatables.net-select/select.bootstrap4.min.css');
    require('style-loader!fine-uploader/fine-uploader/fine-uploader-new.min.css');
    

    This code uses the style-loader plugin for WebPack. Here is the relevant reference:

    https://webpack.github.io/docs/stylesheets.html

    The above code tells WebPack to insert a style tag in the DOM.

  • allanallan Posts: 63,689Questions: 1Answers: 10,500 Site admin
    Answer ✓

    I've edited the post. The forum only allows edits in a relatively short window. There were too many cases of folks changing their question or even removing it in some cases!

    Thanks for the link for the style loader. I'll take a look into the code they are using.

    What I really want is to be able to do require('datatables.net-bs')(...) and that would load jQuery, DataTables, Bootstrap integration for DataTables, both JS and CSS.

    Allan

  • felimartinafelimartina Posts: 4Questions: 0Answers: 0

    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
    
    1. Install datatables.net and its typings
    npm install --save datatables.net
    npm install --save-dev @types/datatables.net
    
    1. [OPTIONAL] Add datatables.net theme (bootstrap theme)
    npm install --save datatables.net-bs
    
    1. [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
    

    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"
        ]
    

    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]]);
                });
            }
        }
    
  • allanallan Posts: 63,689Questions: 1Answers: 10,500 Site admin

    Awesome! Thanks you very much for taking the time to write this up and share it with the community!

    Allan

  • MrTwinklesMrTwinkles Posts: 5Questions: 0Answers: 0

    @felimartina - Thank you so much for your example code. Been struggle for ages to reload the table with different data sets until i found this. The .clear is what i was looking for all along because the destroy alone reuses the previous data set.

  • felimartinafelimartina Posts: 4Questions: 0Answers: 0

    After upgrading Angular to 4.2.4 and using the ng cli I started seeing an error when trying to build the project. After some debugging it turned out to be that one of the scripts in the .angular-cli.json was pointing to the wrong file and was generating builds and ng serve to fail.
    in your .angular-cli.json under the scripts tag you should have this

    "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/js/jquery.dataTables.js", // <= USED TO BE THIS: "../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",
    ],
    

    Hope this helps someone else.

    Working with version 1.10.16 of datatables (2017-10-19)

  • simedsimed Posts: 1Questions: 0Answers: 0

    @felimartina : Thank you so much for your help i can use now DataTables on my Angular APP.

    I still have a problem if you have any idea plz : how can i use Render template with angular 2 ?

    Thank you in advance

  • raymosraymos Posts: 1Questions: 0Answers: 0

    @felimartina : I've registered to this forum just to tell you how much I love you,

    You just solved a problem that got me stuck for 3 days !

    We need more small tutorials like that ! Thanks a lot

  • rajat6563rajat6563 Posts: 2Questions: 1Answers: 0

    @felimartina : I also registered to this forum just to tell you that you are awesome.. Please tell me how can i freeze columns of a table using this. Please guide us more if you have a series of tutorials or youtube channel. Also please post the repo URL so we can better understand.

  • felimartinafelimartina Posts: 4Questions: 0Answers: 0

    @simed
    Sorry for late reply...what do you mean by Render template? If you mean how to custom render a column in datatables then it should be as easy as this:

    columns: [
    { title: 'First Name', data: 'firstName' },
    { title: 'Last Name', data: 'lastName' },
    { title: 'Email', data: 'email' },
    { title: 'Phone Number', data: 'phone' },
    {
    title: 'Birth Date',
    data: 'dob',
    render: function (data, type, row) {
    return moment(row.dob).format('MM/DD/YYYY');
    }
    },
    {
    title: 'Status',
    data: 'disabled',
    render: function (data, type, row) {
    let html = '';
    if (row.disabled) {
    html = '&nbsp<span class="label label-danger">DISABLED</span>';
    } else {
    html = '&nbsp<span class="label label-success">ACTIVE</span>';
    }
    if (row.isAdmin) {
    html += '&nbsp<span class="label label-primary">IS ADMIN</span>';
    }
    return html;
    }
    }
    ],

    I hope it helps...please let me know!

  • hassan12345hassan12345 Posts: 1Questions: 0Answers: 0

    @felimartina

            this.tableWidget.on('select', (e, dt, type, indexes) => {
                this.shipmentSelected.emit(this.shipments[indexes[0]]);
    

    The on method is never getting called. I am using the below to color the selected rows.

    $('#example tbody').on( 'click', 'tr', function () {
        $(this).toggleClass('selected');
    } );
    

    I want to select rows and then press a button which would make an ajax call to delete the data to the server. I want to use the on the method but it's never getting called.

  • jarrettjjarrettj Posts: 13Questions: 4Answers: 0

    Hi,

    Good day.

    Anybody have a working fiddle? Been struggling to get this to work. Thanks.

    Regards.
    JJ

This discussion has been closed.