Skip to content
This repository was archived by the owner on Feb 2, 2025. It is now read-only.

[Changing data with the Angular way] not really work as update data in Datatables #63

Closed
br1anchen opened this issue Aug 27, 2014 · 10 comments

Comments

@br1anchen
Copy link

Hi,

I just start to use both datatables and this angular module (wrapper?). Thanks for the current progress of this module.
The implementation of updating data in angular way in current version v0.1.0, only works for adding data to the previous datatable copy. If the data is changed, or the element in the data array reference to different data object, it will still have the previous data array copy in the datatable.
I try to go through the source code to find solution for this case, but only fixed it in heavily hack way :(

var NGRenderer = function (options) {
        return {
          options: options,
          render: function ($scope, $elem) {
            var finishInit = false;
            var firstCallTimerFlag = false;
            ...
            parentScope.$watch(ngRepeatAttr, function (newValue,oldValue) {

              if (oTable && alreadyRendered && !_isDTOldVersion(oTable) && !finishInit) {
                oTable.ngDestroy();
              }else if(finishInit){// New Hack
                  oTable.clear().destroy();
                  alreadyRendered = true;
                  finishInit = false;
                  firstCallTimerFlag = false;
              }
              // This condition handles the case the array is empty
              if (firstCall) {
                firstCall = false;
                firstCallTimerFlag = true;// New Hack
                $timeout(function () {
                  if (!alreadyRendered) {
                    oTable = _doRenderDataTable($elem, _this.options, $scope);
                    alreadyRendered = true;
                  }
                  finishInit = true;// New Hack
                }, 1000, false);  // Hack I'm not proud of... Don't know how to do it otherwise...
              } else if(!finishInit){
                $timeout(function () {
                  oTable = _doRenderDataTable($elem, _this.options, $scope);
                  alreadyRendered = true;
                  if(!firstCallTimerFlag){ // New Hack
                      finishInit = true;
                  }
                }, 0, false);
              }
            }, true);
          }
        };
      };

Basically I instance two flags: finishInit and firstCallTimerFlag, finishInit is for skiping
oTable.ngDestroy() function to clear the data in the current datatable and reset some initial state falg to make the second time _doRenderDataTable() function still be called.
Moreover with firstCallTimerFalg, it can make after _doRenderDataTable() function, the whole update data process will not be triggered again to restore all the data from previous copy in datatable.

PS: As far as I can understand the problem here is the timing issue as the comment described. Do you have any more information about this timing issue? For example which part of the datatable rendering cause it. Please document this issue for other contributor to notice :)

@l-lin
Copy link
Owner

l-lin commented Aug 27, 2014

Indeed, there is some issue when updating or removing data...
However, you hack solution doesn't seem to work 😞
When adding, updating or removing an item from the array of data, all the data of the DataTable is cleared and it display as is there are no data available for DataTable.

angular_way_change_data

I'll look into this.

For you information, that timer issue is because the ̀$watch` is called twice:

  • once before the array is supplied with data
  • once more when the array is set
    So that timer "hack" is used to prevent to render twice, which would throw an ugly DataTables alert error (that says that we are instanciating the same DT twice).

@br1anchen
Copy link
Author

Thanks, l-lin Yes, I just tested my solution, it works for some scenario. The same thing happens when I update data in the original data array :( I will try to find some solution as well.
Thanks for quick reply :)

@Philmod
Copy link

Philmod commented Sep 8, 2014

Hey guys,

I'm using the 0.1.1 version, but I still have the issue.

If I modify your example to load the full data after 2 seconds, we only see the first item.

.controller('angularWayChangeDataCtrl', function ($scope, DTOptionsBuilder, DTColumnDefBuilder) {
    $scope.persons = [{
          "id": 860,
          "firstName": "Superman",
          "lastName": "Yoda"
      }
    ];
    $scope.dtOptions = DTOptionsBuilder.newOptions().withPaginationType('full_numbers');
    $scope.dtColumnDefs = [
        DTColumnDefBuilder.newColumnDef(0),
        DTColumnDefBuilder.newColumnDef(1),
        DTColumnDefBuilder.newColumnDef(2),
        DTColumnDefBuilder.newColumnDef(3).notSortable()
    ];

    setTimeout(function() {
      console.log('heyyyyy')
      $scope.persons = [{
            "id": 860,
            "firstName": "Superman",
            "lastName": "Yoda"
        }, {
            "id": 870,
            "firstName": "Foo",
            "lastName": "Whateveryournameis"
        }, {
            "id": 590,
            "firstName": "Toto",
            "lastName": "Titi"
        }, {
            "id": 803,
            "firstName": "Luke",
            "lastName": "Kyle"
        }
      ]
    }, 2000);
});

Any idea to fix it?

Thanks,
Philippe

@l-lin
Copy link
Owner

l-lin commented Sep 8, 2014

It seems like you need to use the $timeout service and not the JS native setTimeout function.
I guess it must be because of Angular's digest feature (ie we need to tell Angular to call the watchers).
So it will become something like this:

.controller('angularWayChangeDataCtrl', function ($scope, DTOptionsBuilder, DTColumnDefBuilder, $timeout) {
    $scope.persons = [{
          "id": 860,
          "firstName": "Superman",
          "lastName": "Yoda"
      }
    ];
    $scope.dtOptions = DTOptionsBuilder.newOptions().withPaginationType('full_numbers');
    $scope.dtColumnDefs = [
        DTColumnDefBuilder.newColumnDef(0),
        DTColumnDefBuilder.newColumnDef(1),
        DTColumnDefBuilder.newColumnDef(2),
        DTColumnDefBuilder.newColumnDef(3).notSortable()
    ];

    $timeout(function() {
      console.log('heyyyyy')
      $scope.persons = [{
            "id": 860,
            "firstName": "Superman",
            "lastName": "Yoda"
        }, {
            "id": 870,
            "firstName": "Foo",
            "lastName": "Whateveryournameis"
        }, {
            "id": 590,
            "firstName": "Toto",
            "lastName": "Titi"
        }, {
            "id": 803,
            "firstName": "Luke",
            "lastName": "Kyle"
        }
      ]
    }, 2000);
});

@Philmod
Copy link

Philmod commented Sep 8, 2014

Oh thanks, that works better.

So let's back to my real issue. I'm listening to a socket to get new data. When new data comes in, they appear, but you see "Showing 1 to 1 of 1 entries" and they are not searchable (https://www.dropbox.com/s/yx7dzxpre7kqg2q/Screenshot%202014-09-08%2023.17.11.png?dl=0)

.controller('subscriptionsCtrl', function ($scope, socket, DTOptionsBuilder, DTColumnDefBuilder) {
  $scope.subs = [{
      id: 1
    , object: 'yo'
    , object_id: 'ya'
    , aspect: 'yi'
    , callback_url: 'yy'
  }];

  $scope.dtOptions = DTOptionsBuilder
    .newOptions()
    .withBootstrap();

  $scope.dtColumns = [
    DTColumnDefBuilder.newColumnDef(0),
    DTColumnDefBuilder.newColumnDef(1),
    DTColumnDefBuilder.newColumnDef(2),
    DTColumnDefBuilder.newColumnDef(3),
    DTColumnDefBuilder.newColumnDef(4)
  ];

  socket.on('subscriptions:get', function(data) {
    $scope.subs = data.data;
  });
});

Any idea on this bug?

Thanks,
Philippe

@l-lin
Copy link
Owner

l-lin commented Sep 8, 2014

Have you tried calling $scope.$apply(), ie:

socket.on('subscriptions:get', function(data) {
    $scope.subs = data.data;
    $scope.$apply();
});

@Philmod
Copy link

Philmod commented Sep 8, 2014

Yes, it doesn't help.

@l-lin
Copy link
Owner

l-lin commented Sep 8, 2014

Mmh weird. I'm not too familiar with socket. Are you using angular-socket-io?

I think the problem must come from the fact that the $digest cycle is not triggered.
Can you try the following and tell me if the foobar message is displayed in the console:

.controller('subscriptionsCtrl', function ($scope, socket, DTOptionsBuilder, DTColumnDefBuilder) {
  $scope.subs = [{
      id: 1
    , object: 'yo'
    , object_id: 'ya'
    , aspect: 'yi'
    , callback_url: 'yy'
  }];

  $scope.dtOptions = DTOptionsBuilder
    .newOptions()
    .withBootstrap();

  $scope.dtColumns = [
    DTColumnDefBuilder.newColumnDef(0),
    DTColumnDefBuilder.newColumnDef(1),
    DTColumnDefBuilder.newColumnDef(2),
    DTColumnDefBuilder.newColumnDef(3),
    DTColumnDefBuilder.newColumnDef(4)
  ];

  $scope.$watchCollection('subs', function() {
     console.log('foobar');
  });
  socket.on('subscriptions:get', function(data) {
    $scope.subs = data.data;
    $scope.$apply();
  });
});

@Philmod
Copy link

Philmod commented Sep 9, 2014

Yes, the foobar appeared 3 times, and 2 new data.

foobar 
message :  subscriptions:get 3
foobar 
message :  subscriptions:get 3
foobar 

@l-lin
Copy link
Owner

l-lin commented Sep 9, 2014

I don't know if I can really help you, unless you debug directly on the source of angular-datables, because I don't really know how socket works...
If you have some spare time, can you tell me if thoses 3 lines are indeed executed when your $scope.subs is updated?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants