Datatables with API Storage for selected entries, problems with reload

Datatables with API Storage for selected entries, problems with reload

dvogtdvogt Posts: 4Questions: 1Answers: 0
edited May 2017 in Free community support

TL:DR: Prevent ajax.reload() from calling the "selected" event.

I have an issue regarding a datatable, where i retrieve data from an API, which also serves as a storage for the selection of rows.

This way, when the client will reload the page or wants to edit his selection later on, his selection will be stored. Secondly, we can use the selection in the next steps of a process, without the need of hidden input fields to submit them to the Backend.

Now each time i have a selection inside the datable, comming from an external source like a button or a search field, i will send the selection to the api and call $dataTable.ajax.reload();

This causes the datatables to refresh the content and call the select function on every previously selected row, even though these have changed in the api. I dont want this to happen.

So how can i prevent ajax.reload from calling the "selected" event?

Regards
Dominik

This question has an accepted answers - jump to answer

Answers

  • dvogtdvogt Posts: 4Questions: 1Answers: 0

    I checked after the API call, how many rows are selected by calling console.log($dataTable.rows( { selected: true } ).count());

    Suprisingly the count after deselection is 0. So somehow the "selected"-class is not removed from the row or is set later on in a strange manor.

    Does anyone now how this can happen or how i can solve it?

  • dvogtdvogt Posts: 4Questions: 1Answers: 0
    edited May 2017

    Client Log /JS

    //First request -> Press IndustryID 1 Button to select 5 Locations via API
    multiSelectFromDatatable.js:101 select
    multiSelectFromDatatable.js:299 5 Selections
    multiSelectFromDatatable.js:300 finish sync
    
    //Second Request -> Press IndustryID 1 Button to deselect 5 Locations via API
    multiSelectFromDatatable.js:299 0 Selections
    multiSelectFromDatatable.js:300 finish sync
    
    // error calls for select event, so that prior selected elements will be again called within the select event.
    multiSelectFromDatatable.js:362 Object {id: 1, status: 1, client_id: 1, unique_key: "70e5d78185d2d81", name: "Markt5 Café"…}
    multiSelectFromDatatable.js:260 Send SelectedClientLocation 1
    multiSelectFromDatatable.js:362 Object {id: 11, status: 1, client_id: 1, unique_key: "341a97dcfbf29e0", name: "mySPOT Office I"…}
    multiSelectFromDatatable.js:260 Send SelectedClientLocation 11
    multiSelectFromDatatable.js:362 Object {id: 47, status: 1, client_id: 1, unique_key: "d434c1cc161011c", name: "mySPOT Office IV"…}
    multiSelectFromDatatable.js:260 Send SelectedClientLocation 47
    multiSelectFromDatatable.js:362 Object {id: 61, status: 0, client_id: 1, unique_key: "213b9c781a1cb37", name: "Neuer Standort"…}
    multiSelectFromDatatable.js:260 Send SelectedClientLocation 61
    multiSelectFromDatatable.js:362 Object {id: 62, status: 0, client_id: 1, unique_key: "aa1a0b81305f12f", name: "Noch ein neuer Standort"…}
    multiSelectFromDatatable.js:260 Send SelectedClientLocation 62
    
    

    Server Log

    //First request -> Press IndustryID 1 Button to select 5 Locations via API
    [2017-05-17 18:57:03] dev.INFO: chooseClientLocation Request [{"select":"true","industry_id":"1"}] 
    [2017-05-17 18:57:03] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:03] dev.INFO: Count of selectedClientLocation before setting it [5] 
    [2017-05-17 18:57:04] dev.INFO: Count of selectedClientLocation after retrieving it [5] 
    [2017-05-17 18:57:04] dev.INFO: getClientLocationsForDatatable Request Count of Selected ClientLocation [5] 
    
    //Second Request -> Press IndustryID 1 Button to deselect 5 Locations via API
    [2017-05-17 18:57:07] dev.INFO: chooseClientLocation Request [{"select":"false","industry_id":"1"}] 
    [2017-05-17 18:57:07] dev.INFO: Count of selectedClientLocation after retrieving it [5] 
    [2017-05-17 18:57:07] dev.INFO: before diff [5] 
    [2017-05-17 18:57:07] dev.INFO: after diff [0] 
    [2017-05-17 18:57:07] dev.INFO: Count of selectedClientLocation before setting it [0] 
    [2017-05-17 18:57:07] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:07] dev.INFO: getClientLocationsForDatatable Request Count of Selected ClientLocation [0] 
    
    // this is where shit happens. Normaly this request should not happen. Somehow DataTables manages to call the select event for each prior selected datatables entry.
    [2017-05-17 18:57:08] dev.INFO: chooseClientLocation Request [{"select":"true","client_location_id":"47"}] 
    [2017-05-17 18:57:08] dev.INFO: chooseClientLocation Request [{"select":"true","client_location_id":"1"}] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation before setting it [1] 
    [2017-05-17 18:57:08] dev.INFO: chooseClientLocation Request [{"select":"true","client_location_id":"11"}] 
    [2017-05-17 18:57:08] dev.INFO: chooseClientLocation Request [{"select":"true","client_location_id":"62"}] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation before setting it [1] 
    [2017-05-17 18:57:08] dev.INFO: chooseClientLocation Request [{"select":"true","client_location_id":"61"}] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation before setting it [1] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation before setting it [1] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation after retrieving it [0] 
    [2017-05-17 18:57:08] dev.INFO: Count of selectedClientLocation before setting it [1] 
    
  • allanallan Posts: 61,723Questions: 1Answers: 10,108 Site admin
    Answer ✓

    This is how Select retains the selected rows when ajax.reload() is called. Basically it will attempt to find the ids of the selected rows and then once the reload is complete it will then reselect them.

    I can't be sure without a test case, but I suspect that is what you are seeing.

    This isn't really a way to disable that at the moment - it isn't a configurable option. You could not have row ids in which case it wouldn't find anything. Or modify the library and remove that code (assuming I'm correct and it is that which is causing the issue).

    Allan

  • dvogtdvogt Posts: 4Questions: 1Answers: 0

    I think you have interpreted it right.

    For now i just implemented my own local data-structure, which keeps track which rows have been selected and which not.

    Because this problem only occurred when selecting multiple entries at the same time via AJAX, i now return the selected/deselected entries from my API-endpoint. According to the call, I then add/remove the specified values from the local data-structure.

    Additionally I decided to distinguish between select and user-select event. When the select event is being called, I check if the value exists in the local data-structure. If not, I deselect it. Only user-select Event is then allowed to add data to the local data-structure.

    Also wanted to paste my code. Might help someone in the future! (Hello person from the future)

    Data-Structure

            function SelectedValues(){
                this.values = [];
            }
    
            SelectedValues.prototype.addValue = function(value){
                if(!this.hasValue(value)){
                    console.log("add Value to selectedValues", value);
                    this.values.push(parseInt(value));
                } else {
                }
            };
    
            SelectedValues.prototype.removeValue = function(value){
                value = parseInt(value);
                var index = this.values.indexOf(value);
                if(index > -1){
                    console.log("remove Value from selectedValues", value);
                    this.values.splice(index, 1);
                }
            };
    
            SelectedValues.prototype.hasValue = function(value){
                value = parseInt(value);
                return this.values.indexOf(value) > -1;
            };
    
            SelectedValues.prototype.getValues = function(){
                return this.values;
            };
    

    Event-Handler

                $dataTable.on('deselect', function(e, dt, type, indexes){
                    console.log("Event deselect");
                    console.log("Event deselect - Indexes:", indexes);
                    if(status.initialized && !status.sync_in_progress){
                        var data = $dataTable.rows(indexes).data();
                        data.each(function(item){
    
                            console.log("Event deselect - Item:", item.id);
                            if(selectedValues.hasValue(item.id)) {
                                selectedValues.removeValue(item.id);
                                sendDeselectedClientLocationToApi(item.id);
                            }
                        })
                    }
                });
    
                $dataTable.on('select', function(e, dt, type, indexes){
                    console.log("Event select");
                    var data = $dataTable.rows(indexes).data();
                    var rows = $dataTable.rows(indexes)[0];
                    console.log("rows", rows);
    
                    var $indexes = [];
                    data.each(function(item, index){
                        if(!selectedValues.hasValue(item.id)){
                            console.log("Item is not selected in selectedValues", item.id);
                            console.log("Item Rows[index]", rows[index]);
                            if(rows[index] !== undefined){
                                $dataTable.rows(rows[index]).deselect();
                            }
                        }
                    });
                    return false;
                });
    
                $dataTable.on('user-select', function(e, dt, type, cell){
                    console.log("Event user-select");
                    if(status.initialized && !status.sync_in_progress){
                        var data = $dataTable.rows(cell[0][0].row).data();
    
                        data.each(function(item){
                            if(!selectedValues.hasValue(item.id)){
                                selectedValues.addValue(item.id);
                                sendSelectedClientLocationToApi(item.id);
                            }
                        });
                    }
                });
    
    

    Sync-Progress-Events

                    $container.on('start-sync-progress', function(){
                        counter++;
                        $loadingContainer.show();
                        status.sync_in_progress = true;
                    });
    
                    $container.on('finish-sync-progress', function(){
    
                        console.log("finish sync");
                        counter--;
                        if(counter == 0){
                            $loadingContainer.hide();
                            status.sync_in_progress = false;
                        }
                    });
    

    API-Calls

                function selectValuesByGroupIdFromAPI(groupId){
                    $container.trigger('start-sync-progress');
                    console.log("Send SelectedClientLocationByGroup to API", groupId);
                    var url = settings.chooseClientLocationApiUrl;
                    $.ajax({
                        method: "POST",
                        url: url,
                        data: {
                            'select': true,
                            'industry_id': groupId
                        },
                        success: function(result){
                            var data = result.data;
    
                            if(data.hasOwnProperty("clientLocations") && data["clientLocations"]){
                                var clientLocations = data.clientLocations;
                                clientLocations.forEach(function(item){
                                    if(!selectedValues.hasValue(item.id)) {
                                        selectedValues.addValue(item.id);
                                    }
                                });
                            }
    
                            if(data.hasOwnProperty("selectedCount") && data["selectedCount"]){
                                selectedTargetCount = data.selectedCount;
                                $container.trigger('update-count', [selectedTargetCount]);
                            }
    
                            $dataTable.ajax.reload(function(){
                                $container.trigger('finish-sync-progress');
                            }, false);
                        }
                    });
                }
    
                //get all ids from backend identified by the group id
                function deselectValuesByGroupIdFromAPI(groupId){
                    $container.trigger('start-sync-progress');
                    console.log("Send DeselectedClientLocationByGroup to API", groupId);
                    var url = settings.chooseClientLocationApiUrl;
                    $.ajax({
                        method: "POST",
                        url: url,
                        data: {
                            'select': false,
                            'industry_id': groupId
                        },
                        success: function(result){
                            var data = result.data;
    
                            if(data.hasOwnProperty("clientLocations") && data["clientLocations"]){
                                var clientLocations = data.clientLocations;
                                clientLocations.forEach(function(item){
                                    if(selectedValues.hasValue(item.id)) {
                                        selectedValues.removeValue(item.id);
                                    }
                                });
                            }
    
                            if(data.hasOwnProperty("selectedCount") && data["selectedCount"]){
                                selectedTargetCount = data.selectedCount;
                                $container.trigger('update-count', [selectedTargetCount]);
                            }
    
                            $dataTable.ajax.reload(function(){
                                $container.trigger('finish-sync-progress');
                            }, false);
                        }
                    });
                }
    
  • allanallan Posts: 61,723Questions: 1Answers: 10,108 Site admin

    Thanks for posting your code. Good to hear you've got it working the way you need now!

    Regards,
    Allan

This discussion has been closed.