1

I'm using this library: http://l-lin.github.io/angular-datatables

All is well - I rendered the table Angular way - until the data is large enough to make the performance suffers (only 1,000+ rows).

The solution is to render with ajax or promise as suggested here: angular-datatables the deferRender not working

So for the past three days, I'm stuck at how to transform below codes from angular way to promise

ANGULAR WAY

view.html

<table datatable="ng" dt-options="dtOptions" class="table table-striped table-bordered">
                                <thead>
                                    <tr>
                                      <th>No</th>
                                        <th>Issue</th>
                                        <th>Meeting No.</th>
                                          <th>Department/Unit</th>
                                   </tr>
                                </thead>
                                <tbody>
                                    <tr ng-repeat="(key, value) in issueList | groupBy : 'IssueId' | reverse track by $index">
                                      <td ng-class="{read : value[0].AnswerStatus == 1}">{{$index+1}}</td>
                                        <td>{{value[0].Issue}}</td>
                                        <td>{{value[0].MeetingNo}}</td>
                                        <td>
                                          <table class="table table-striped table-bordered" cellspacing="0" width="100%">
                                            <tbody>
                                              <tr ng-repeat="x in value">
                                              <td width="80%">{{x.Department}}/{{x.Unit}}</td>
                                            <td>  <a class="btn btn-default waves-effect m-b-5" ng-click="sendDetail(x.IssueId,x.UnitId)"><i class="fa fa-folder-open"></i> Show </a></td>
                                            </tr>
                                            </tbody>
                                          </table>
                                          </td>

                                    </tr>

                    </tbody>
                            </table>

view.js

$scope.dtOptions = 
DTOptionsBuilder.newOptions() 
.withOption('stateSave', true)
.withOption('stateDuration', -1)
.withOption('deferRender', true);

$http({
method: 'GET',
url: 'http://issue.json'})
.then(function(response) {
    $scope.issueList = response.data;
  });

issue.json

[{
    "IssueId": "1",
    "MeetingNo": "1",
    "Issue": "Issue title 1",
    "Content": "Content 1",
    "AnswerStatus": null,
    "UnitId": 1,
    "Unit": "Unit 1",
    "DepartmentId": 1,
    "Department": "Department 1"
}, {
    "IssueId": "2",
    "MeetingNo": "1",
    "Issue": "Issue title 2",
    "Content": "Content 2",
    "AnswerStatus": null,
    "UnitId": 5,
    "Unit": "Unit 5",
    "DepartmentId": 1,
    "Department": "Department 1"
}, {
    "IssueId": "2",
    "MeetingNo": "1",
    "Issue": "Issue title 2",
    "Content": "Content 2",
    "AnswerStatus": 1,
    "UnitId": 6,
    "Unit": "Unit 6",
    "DepartmentId": 1,
    "Department": "Department 1"

}]

Below code is my halfway transformation

PROMISE WAY

view.html

<table datatable="" dt-options="dtOptions" dt-columns="dtColumns" class="table table-striped table-bordered">

                            </table>

view.js

$scope.dtOptions = 
    DTOptionsBuilder.fromFnPromise(function() {
        var defer = $q.defer();
            $http({
          method: 'GET',
          url: 'http://issue.json'
      }).then(function(result) {
            defer.resolve(result.data);
        });
        return defer.promise;
    })    
    .withOption('stateSave', true)
    .withOption('stateDuration', -1)
    .withOption('deferRender', true); //the very reason to use promise for performance booth

$scope.dtColumns = [
DTColumnBuilder.newColumn(null).withTitle('No')
        .renderWith(function(data, type, full, meta) {
            return (meta.row+1);
        }),
 DTColumnBuilder.newColumn('Issue').withTitle('Issue'),
 DTColumnBuilder.newColumn('MeetingNo').withTitle('Meeting No.'),
 DTColumnBuilder.newColumn('Department').withTitle('Department/Unit'), 
 ];

As you can see, the first column is missing the styling needed for AnswerStatus == 1 and the last column is missing the combine value of Department + '/' + Unit and the button to navigate to another page.

Also please note that the angular way is using groupBy which further complicates my problems :(

Please help. Thanks for your time.

UPDATE:

Actually, I also considering alternative solution, but still facing a road block. I am considering to refactor the json with this:

   Array.prototype.groupBy = function(prop) {
    return this.reduce(function(groups, item) {
      const val = item[prop]
      groups[val] = groups[val] || []
      groups[val].push(item)
      return groups
    }, {})
  }

 $scope.dtOptions = 
DTOptionsBuilder

.fromFnPromise(function() {
    var defer = $q.defer();
        $http({
      method: 'GET',
      url: 'http://issue.json'
  }).then(function(result) {

        var d = result.data.groupBy('IssueId');
        var arr = Object.values(d);
        console.log(arr);
        defer.resolve(arr);
    });
    return defer.promise;
})
.withOption('stateSave', true)
.withOption('stateDuration', -1)
.withOption('deferRender', true);

which results in a new array of numbered arrays like this (console.log(arr)):

    [
[{
        "IssueId": "1",
        "MeetingNo": "1",
        "Issue": "Issue title 1",
        "Content": "Content 1",
        "AnswerStatus": null,
        "UnitId": 1,
        "Unit": "Unit 1",
        "DepartmentId": 1,
        "Department": "Department 1"
    }], 
[{
        "IssueId": "2",
        "MeetingNo": "1",
        "Issue": "Issue title 2",
        "Content": "Content 2",
        "AnswerStatus": null,
        "UnitId": 5,
        "Unit": "Unit 5",
        "DepartmentId": 1,
        "Department": "Department 1"
    }, {
        "IssueId": "2",
        "MeetingNo": "1",
        "Issue": "Issue title 2",
        "Content": "Content 2",
        "AnswerStatus": 1,
        "UnitId": 6,
        "Unit": "Unit 6",
        "DepartmentId": 1,
        "Department": "Department 1"

    }]
]

but I don't know how to proceed with the new array.

Iyas
  • 520
  • 1
  • 11
  • 40
  • I think this question go far beyond the scope of SO. As isolated problems they are fine and easy to solve, but you ask for a complete refactoring programming task that helps not one in the future. – davidkonrad Sep 23 '18 at 07:41
  • For the first column, use `createdCell`. For the "group by" you must reorganize the JSON so it holds `IssueId`'s which holds child objects - then you can render out the column with all its child content. You cannot "GroupBy" this way in DataTables, the "normal" approach would be to use child rows. As for the `ng-click` you need to use `$compile` in `createdRow` (or simply not use a directive at all, just use a normal click handler) It is certainly doable to refactor the whole thing rather painless, it would just be a looooong answer not suitable for SO. – davidkonrad Sep 23 '18 at 07:48
  • I beg to differ, basically what I'm asking is to be able to render complex datatables not using angular. It is a genuine use case, and I expect to use it in my future projects. Thanks for you time and suggestion. At least I have something to fall back if my attempt to render complex datatables is not successful. – Iyas Sep 23 '18 at 07:48
  • See my next comment. It is not a bad question at all, just too overwhelming :) OK, I'll see what I can do, made a plunkr anyway and just realized that I needed to actually rethink your code, but can create a promised' version. – davidkonrad Sep 23 '18 at 07:52
  • Thanks :) Since you just mentioned to reorganise the json, I've updated the question with my alternative attempt. – Iyas Sep 23 '18 at 08:05

1 Answers1

1

Well, a plunkr with the original version is here http://plnkr.co/edit/Q2ob8gynNTXXId9pxnzz?p=preview

Here is how you can reorganise the JSON so it is grouped

$scope.dtOptions = 
  DTOptionsBuilder.fromFnPromise(function() {  
    var defer = $q.defer();
    $http({
      method: 'GET',
      url: 'issues.json'
    }).then(function(result) {
      var data = [];
      result.data.forEach(function(i) {
        var item = data.find(item => item.IssueId == i.IssueId)
        if (item) {
          item.childs.push(i)
        } else {
        data.push({ 
          IssueId: i.IssueId, 
          Issue: i.Issue,
          MeetingNo: i.MeetingNo,      
          childs: [i] 
        })
      }
    }) 
    defer.resolve(data);
  })
  return defer.promise;
})    

Here is how you could setup the columns

$scope.dtColumns = [
  DTColumnBuilder.newColumn(null).withTitle('No')
    .withOption('createdCell', function(cell, cellData, rowData, rowIndex, colIndex) {
      if (rowData.AnswerStatus == 1) $(cell).addClass('read')
    })  
    .renderWith(function(data, type, full, meta) {
      return (meta.row+1);
    }),

  DTColumnBuilder.newColumn('Issue').withTitle('Issue'),
  DTColumnBuilder.newColumn('MeetingNo').withTitle('Meeting No.'),

  DTColumnBuilder.newColumn(null)
    .withTitle('Department/Unit') 
    .renderWith(function(data, type, row, meta) {
       var html = '<table><tbody>';
       row.childs.forEach(function(child) {
         html += '<tr><td>'+child.Department+'/'+child.Unit+'</td></tr>'
         html += '<tr><td><button ng-click="buttonClick()">click</button></td></tr>'
       }) 
       html += '</tbody></table>'
       return html
    })
    .withOption('createdCell', function(cell, cellData, rowData, rowIndex, colIndex) {    
       $compile(cell)($scope);  
    })
]

Simply use createdCell to post process columns. Use $compile in order to get other angular directives to work. You have some issues you not have thought about in the original code, like which AnswerStatus to choose.

http://plnkr.co/edit/izRyvvZr4Zo7ioxiOmEI?p=preview

davidkonrad
  • 83,997
  • 17
  • 205
  • 265
  • 1
    Very perfect solution! Thanks mate. And yes you are right about the AnswerStatus issue. – Iyas Sep 23 '18 at 22:07
  • Well, thanks :) And the question and the answer will never be upvoted and only be visited by coincidence. Just saying :) – davidkonrad Sep 23 '18 at 22:38