Using Angular-Datatables Notes

These notes are based on angular-datatables v8.0.0 


Using Angular Component

In order to get a angular component say to run you need to create the code in the same scope. That's how I managed to get it running. So for example you have to create a model via angular and attach it to a jquery event. What your doing is in the definition of the datatable is passing the logic in at that point on how to run.

 import { ComponentOnDestroyOnInitViewChildAfterViewInit } from '@angular/core';

import { MetaTitle } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { NgbDateStructNgbModal } from '@ng-bootstrap/ng-bootstrap';
import { formatDate } from "@angular/common";
import { DataTableDirective } from 'angular-datatables';
import { Subject } from 'rxjs';
import { NgbDateFRParserFormatter } from './../../../dateformat';
import { MRPDetailComponent } from '../MRPDetail/MRPDetail.component';
import { PickerComponent } from '../picker/picker.component';
import { CommentComponent } from '../Comment/Comment.component';

@Component({
  providers: [NgbDateFRParserFormatter]
})
export class ExampleComponent implements AfterViewInitOnDestroyOnInit {
  ngOnDestroy(): void {
    this.dtTrigger.unsubscribe();
  }
  @ViewChild(DataTableDirective, { staticfalse })
  datatableElement: DataTableDirective;
  dtOptions: DataTables.Settings = {};
  rawPurchaseOpts: RawPurchaseOpts;
  currentDate: Date;
  orderBy: NgbDateStruct;
  dueDate: NgbDateStruct;
  public isCollapsed = true;

  dtTrigger: Subject<any> = new Subject();

  constructor(
    private meta: Meta,
    private modalService: NgbModal,
    private formatter: NgbDateFRParserFormatter,
    private titleService: Titleprivate http: HttpClient) { }
  public utils: rowData[];
  public cities: any[];
  dropdownSettings: any = {};
  dropdownSettingsTypes: any = {};


  ngOnInit() {
    this.currentDate = new Date();
    this.orderBy = { day: this.currentDate.getDate(),
     month: this.currentDate.getMonth()+1, year: this.currentDate.getFullYear() };
    this.titleService.setTitle('Topiderm MFG - RawPurchases');
    this.meta.addTag({
      name: 'Topiderm MFG',
      content: 'Raw Purchases'
    });
    this.dropdownSettings = {
      singleSelection: false,
      idField: 'id',
      textField: 'name',
      selectAllText: 'Select All',
      unSelectAllText: 'UnSelect All',
      itemsShowLimit: 3,
    };
   
    this.meta.updateTag(
      {
        name: 'description',
        content: 'Topiderm Raw Purchase Orders'
      });

    const that = this;

    this.dtOptions = {
      ajax: (_dataTablesParameters: any, callback) => {
        that.http.get('MRP/RawComplete')
          .subscribe(result => {
            that.rawPurchaseOpts = result as any;
            that.http.get('api/').subscribe(result => {
              that.utils = result as MGUtil[];

              const format = 'MM/dd/yyyy';
              const locale = 'en-US';

              that.utils.forEach(function (part, _index, _theArray) {
                part.requestDate = formatDate(part.requestDate, format, locale);
                part.orderByDate = formatDate(part.orderByDate, format, locale);
              });
              callback({
                recordsTotal: that.utils.length,
                recordsFiltered: 0,
                data: result as MGUtil[]
              });
            }, error => console.error(error));

          }, error => console.error(error));
      },
      "columnDefs": [
        {
          "render"function (data) {
            return '<input type="checkbox" value="' + data + '"></input>';
          },
          orderable: false,
          width: "6px",
          "targets"0
        },
        {
          "render"function (data) {
            return '<input type="checkbox" value="' + data + '"></input>';
          },
          orderable: false,
          width: "6px",
          "targets"1
        }, {
          "render"function (data) {
            return '<input type="checkbox" value="' + data + '"></input>';
          },
          orderable: false,
          width: "6px",
          "targets"2
        },
        {
          "render"function (data, _type, row) {
            return '<a class="modalList" href="" onclick="return false;">' + data + '</a><br/><small>' + row["itemDesc"] + '</small>';
          },
          "targets"3
        },
        {
          "render"function (data, type, row) {
            const result = that.rawPurchaseOpts.vendors.filter(vendor => vendor.id == row["vendor"]);
            var body = "";
            $.each(result, function (_key, value) {
              if (_key === 0)
                body += '<option selected value="' + value.id + '">' + value.name + '</option>';
              else
                body += '<option value="' + value.id + '">' + value.name + '</option>';
            });
            var start = '<select id="test" class="form-control form-control-sm">' +
              '<option value=""> Please Select </option>';
            var end = "</select>";
            return start + body + end;
          },
          orderable: false,
          "targets"4
        },
        {
          "render"function (data, type, row) {
            return '<input style="max-width:80px;" class="form-control form-control-sm" type="text" value="' + data + '"></input>';
          },
          "width""80px",
          orderable: false,
          "targets"6
        },
        {
          "render"function (data, type, row) {
            return ' <div class="input-group">' +
              '<input class="form-control form-control-sm" placeholder="dd/mm/yyyy" name="orderBy" disabled value="' + data + '" >' +
              '<div class="input-group-append">' +
              '  <button class="btn btn-outline-secondary btn-sm calendar" type="button">' +
              '    <i class="fas fa-calendar" id="searchOrderByDate"></i>' +
              '  </button>' +
              '</div>' +
              '</div>';
          },
          "targets"8
        },
        {
          "render"function (data, type, row) {
            return '<button class="btn btn-outline-secondary redirect btn-sm">' +
              ' <i class="far fa-comments" data-toggle="modal" data-target="#commentModal" data-id="3" data-comment="" title="" "=""></i></span>';
          },
          orderable: false,
          "targets"9
        },
      ],
      order: [[3'asc']],
      //DOCS: https://datatables.net/forums/discussion/21709/orderable-false-on-target-0-first-col-not-working-until-another-column-is-sorted
      columns: [
        { data: 'selected', orderable: false }, //Does nothing without order(3)
        { data: 'seperate' },
        { data: 'blanket' },
        { data: 'itemNo' },
        { data: 'vendor' },
        { data: 'loc' },
        { data: 'qty' },
        { data: 'orderByDate', className: "text-nowrap" },
        { data: 'requestDate' },
        { data: 'comment' }],
      rowCallback: (row: Node, data: any[] | Object, index: number) => {

        $('.calendar', row).unbind('click');
        $('.calendar', row).bind('click', (_event) => {
          this.detailNum = "test";
          const modalRef = this.modalService.open(PickerComponent, { size: 'md' });
          modalRef.componentInstance.user = this.detailNum;

          return row;
        });

        $('.redirect', row).unbind('click');
        $('.redirect', row).bind('click', (_event) => {
          this.detailNum = "test";
          this.modalService.open(CommentComponent, { size: 'lg' }).result.then((result) => {
          }, (reason) => {
            console.log(reason);
          });

          return row;
        });

        $('.modalList', row).unbind('click');
        $('.modalList', row).bind('click', (_event) => {
          console.log(data["itemNo"]);
          this.detailNum = data["itemNo"];
          const modalRef = this.modalService.open(DetailComponent, { size: 'lg' });
          modalRef.componentInstance.detailNum = this.detailNum;

          return row;
        });
      },
      autoWidth: false,
      responsive: true,
      "lengthMenu": [510255075100]
    };
    this.dtTrigger.next();
  }


  ngAfterViewInit(): void {
    this.dtTrigger.next();
    this.datatableElement.dtInstance.then((dtInstance: DataTables.Api) => {
      dtInstance.columns().every(function () {
        const that = this;
        $('input'this.footer()).on('keyup change'function () {
          if (that.search() !== this['value']) {
            that
              .search(this['value'])
              .draw();
          }
        });
      });
    });
  }

  rerender(): void {
    const that = this;
    this.dtOptions.ajax =
      (_dataTablesParameters: any, callback) => {
        that.http.get('/api', {
          params: {
            data: Data
          }
        })
          .subscribe(result => {
            that.utils = result as rawData[];

            const format = 'dd/MM/yyyy';
            const locale = 'en-US';

            that.utils.forEach(function (part, _index) {
              part.requestDate = formatDate(part.requestDate, format, locale);
            });
            callback({
              recordsTotal: that.utils.length,
              recordsFiltered: 0,
              data: result as rawData[]
            });
          }, error => console.error(error));
      };

    this.datatableElement.dtInstance.then((dtInstance: DataTables.Api) => {
      // Destroy the table first
      dtInstance.destroy();
      // Call the dtTrigger to rerender again
      this.dtTrigger.next();
    });
  }
}

Limitations

When I picked this library I never knew how complex the ui would get and it was more about picking a library that my colleagues could use alongside me without issue. Datatables is a great library for its timing however there are a lot of things that datatables can’t do out the gate. 

  • Pipes

  • Components cell rendering

  •  Angular Scope

Not to mention the extra space it takes up including datatables & jquery in order to get it setup. It is because of this I recommend ag-grid since the limitations in angular quick pile on when you're trying to manage a large scale application. However if you had to do it I would recommend filtering the data and formatting yourself then adding in jquery code to handle and clicks inside the cells.


Also with webpack (tool used to combine scripts) it exceeds the recommended limit for es5 but this could be due to the fact that we are converting scripts to es5 rather then es6 in an efficient manner.  


"node_modules/datatables.net/js/jquery.dataTables.js",              "node_modules/datatables.net-bs4/js/dataTables.bootstrap4.min.js",           "node_modules/datatables.net-responsive-dt/js/responsive.dataTables.js",             "node_modules/datatables.net-responsive/js/dataTables.responsive.js",


Moving forward under any angular projects we should get the most out of it and any jquery based libraries we should look for similar packages to replace them. I was able to use components and pass the angular scope if you will to handle my data.



Popular posts from this blog

UI-Bootstrap Collapsible Sticky Footer

Installing Windows on Acer chromebook 15 cb3-532

Aspetcore: NLog with Postgresql