remove() and destroy() won't fully work- Also, can't create second table after remove()/destroy()

remove() and destroy() won't fully work- Also, can't create second table after remove()/destroy()

JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0

I would like to be able to create a second table after removing/destroying the first table.
The flow is Map Selection > Ajax > GeoServer > Feature Data Comes Back > DataTables table created

Then I have a "clearAll()" function where I remove the map selection and attempt to destroy the table in order for another selection to be made and another table to be created.

This removes styling but leaves the data: $("#example").DataTable().destroy();
This hide/removes the entries: $("#example").remove(); But the "Show entries" dropdown box is stil there.

However, using both of these together effectively clears everything of the screen.

NOW, if I try to make another map selection, and thus generate a new table I get this error:

TypeError: Cannot read property 'outerHTML' of null
    at GenerateTable (index.html:174)
    at HTMLInputElement.onclick (index.html:82)

I am able to select features like normal and everything works fine. But it's still showing as "null" despite the fact that I'm logging to the console all changes. It's relying on a layer to be active (that info get's sent to the ajax link), but despite the fact that I've verified via console that a layer is active, it's still showing as null. So, is there another way to go about this?

This question has accepted answers - jump to:

Answers

  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28
    edited July 2017 Answer ✓

    I think the first thing to do is if you're destroying the table and need the DOM elements to go also, pass true to the destroy function, this removes the DataTables as well as the DOM elements for it.

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0

    Would I just set that up like this?

    $("#featureTable").DataTable().destroy(true);
    

    I'm not sure how else to set it to true. I tried that and it did not work.

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0
    edited July 2017

    EDIT... This does not actually work correctly.

    I have it set up like this;

    when a user turn on a map layer.. for instance a "Cities" layer.. i have that assigned to a variable called "activeOverlay".

    User makes selection on the map and the table is created via an Ajax call to GeoServer.

    Now, when that layer is toggled off, activeOverlay is set to "null" until another layer is turned on. For instance, "States" Layer... activeOverlay becomes "States".

    BUT.. even though the selection/Ajax/GeoServer stuff still works and sees activeOverlay as "States" now... DataTables does not... It still has activeOverlay as "Cities" or "Null".

    The activeOverlay variable IS global.

    Nevermind.. I got it. Right at the point where you create the table, you need to add that in like so:

                         $(document).ready(function() {
                                
                                  var columns = [];
                                  var prop = data.features[0].properties;
                                  for (var i in prop) {
                                      columns.push({data: 'properties.' + i, title: i});
                                  }                             
                                   $('#featureTable').DataTable( {
                                        data: data.features,   //get the data under features
                                        columns: columns,
                                       destroy: true  <<<<<<<<<-------- Right there!
    
                                    } );
                                
                                    } );
    

    After I destroy one table and try to create another.. I get this error:

    jquery-3.2.1.js:3869 Uncaught TypeError: Cannot read property 'mData' of undefined
        at HTMLTableCellElement.<anonymous> (jquery.dataTables.min.js:90)
        at Function.each (jquery-3.2.1.js:362)
        at jQuery.fn.init.each (jquery-3.2.1.js:157)
        at HTMLTableElement.<anonymous> (jquery.dataTables.min.js:90)
        at Function.each (jquery-3.2.1.js:362)
        at jQuery.fn.init.each (jquery-3.2.1.js:157)
        at jQuery.fn.init.m [as dataTable] (jquery.dataTables.min.js:82)
        at jQuery.fn.init.h.fn.DataTable (jquery.dataTables.min.js:167)
        at HTMLDocument.<anonymous> (index.html:725)
        at mightThrow (jquery-3.2.1.js:3583)
    
  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28

    When you set activeOverlay to null, datatables still thinks there is a table as it has it's own internal structure for tables created with .DataTable(). Setting your activeOverlay to null does not destroy your datatable, it only means you've changed your reference to it.

    (The the destroy option is not the same as the destroy api function)

    When you close the activeOverlay you need to also call destroy on the DataTable you created previously if it's no longer needed. Essentially, it sounds like you're losing track of tables that have been created and not cleaning up those that are done with before trying to replace them with a different table.

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0
    edited July 2017

    This is my delete code... I thought that it does what you're suggesting already...?

                    map.on('draw:deleted', function(){ 
                        selectionCounter = 1;                     
                        isControlNull = true;    
                        selectionControl.remove();
                        resetMapSelections();
                        sidebar.hide(); 
                        activeOverlay = null;    
                        $("#featureTable").DataTable().destroy();
                        $("#featureTable").remove();
                        });
    

    This removes all data from the leaflet sidebar (where my table is placed)... BUT..
    when I try to make a new table.. despite the fact that i turn activeOverlay on again, the table is always blank. It will not create a second table.

    Do I have to do anything with the container? It's set up like this:

            <div class="container" id="tableContainer">
                <table id="featureTable" class="display" width="100%">
                </table>
            </div>
    
    
  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28
    edited July 2017

    Hmm, when you call $("#featureTable").remove() the table node is no longer in the DOM, do you create a new #featureTable node before you try to create a new datatable?

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0
    edited July 2017

    I believe I do.. unless there is something I'm missing. The table are created after an ajax call to GeoServer. In the ajax portion.. there is the "success" inscructions like below:

                        $.ajax({
                                    url : owsrootUrl + L.Util.getParamString(parameters),
                                    dataType : 'jsonp',
                                    jsonpCallback : 'getJson',
                                    success : handleJson
                                    });
                                });
    
    

    The "handleJson" funtion includes (among other instructions) the code to create a table. It's the first thing that happens in the handleJson function:

                            $(document).ready(function() {  
                                  var columns = [];
                                  var prop = data.features[0].properties;
                                  for (var i in prop) {
                                      columns.push({data: 'properties.' + i, title: i});
                                  }                             
                                   $('#featureTable').DataTable( {
                                        data: data.features,   //get the data under features
                                        columns: columns,
                                       destroy: true
                                    } );                           
                                    } );
    

    I've placed console.log() code right inside the handleJson function and then right inside the table creation code (right under the (document).ready) portion just to make sure it was being hit each time. Console shows both portions of code are hit, but no new table is generated. :-(

  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28
    edited July 2017

    $('#featureTable').DataTable(...) says: Create a datatable on the node with the id featureTable. This means the jQuery selector must find a node with that id. If you have called $("#featureTable").remove() there will NOT be a node with that id, you've removed it from the DOM, no nodes from the selector == no datatable. You can check this by Inspect-ing the page to see the live DOM.

    You haven't shown where you create the #featureTable node again after calling .remove() on it.

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0

    It's created again when another ajax/geoserver call is made. Is that what you're referring to??
    1)I press a button to clear features/table which calls remove().. DOM is cleared.
    2) Another feature is selected on the map.. thus another ajax call which runs the handleJson function and another table is created with the #featureTable id.

    *****It should be noted... as long as I do not press the clear button, new tables are created fine. They just replace the old tables. BUT, as soon as I call the remove() function, no more table will be created.

  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28
    edited July 2017

    I'm sorry, I've become a bit lost now. calling remove() removes the element, do not remove the table element if you want to do $('#featureTable') again, with no results to the jQuery selector there are no elements to call .DataTable() on and no datatables will be created. This is a jQuery thing, not datatables.

    If you must remove() the element with that id make sure you create a table with that id in the DOM before trying to call $('#featureTable').DataTables() so there is an element to create a datatable on. This is true whether you call .remove() or table.destroy(true) (passing true calls remove() without you having to do it yourself)

    The problem you're encountering does not appear to be with datatables but with DOM management and jquery selectors.

    Also, if your table definition is exactly same each time it is used (except with different data) there's always clear and row.add to change the content. Then you can always use jquery show/hide functions on your container to change the visibility of the table.

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0

    Thanks for your help, i really do appreciate it. I'm a novice at JavaScript so this is sort of a trial by fire for me. Here is all my code pertaining to how I'm using these tables and their creation.

            <div id="map"></div>
                <div class="container" id="tableContainer">
                <table id="featureTable" class="display" width="100%">
                </table>
            </div>
    
    //Adds the table to a Leaflet Sidebar (v1)
                function GenerateTable(){
                    $('#tableContainer').attr("style", "display: inline !important");
                    var featureTable = document.getElementById("tableContainer");
                        map.addControl(sidebar);
                        sidebar.setContent(featureTable);
                        sidebar.toggle();  
                    }
    
    //Using Leaflet Draw, a bounding box or polygon is set up and sent via Ajax to GeoServer and all the points within that area 
    //are returned
                                           //Different CQL Filter depending on what "type" is selected in leaflet draw
                                if(type === 'rectangle') {
                                    customParams = {CQL_FILTER : 'BBOX(geom, ' + toBBox + ')'}
                                }
                                else if(type === 'polygon') {
                                    customParams = {CQL_FILTER : 'WITHIN(geom, POLYGON((' + polyCQL + ', ' + polyCQLArray[1] + ' ' + polyCQLArray[0] + ')))'}
                                } else if(type === 'circle') {
                                    customParams = {CQL_FILTER : 'WITHIN(geom, POLYGON((' + circCQL + ', ' + circCQLArray[1] + ' ' + circCQLArray[0] + ')))'}
                                }
    
    
               //Ajax link set up and connect to GeoServer with all parameters set
    
                      map.on('draw:created', function(e) {  
                                console.log(activeOverlay
    
                                var parameters = L.Util.extend(defaultParameters, customParams);
                                var url = owsrootUrl + L.Util.getParamString(parameters)
                                $.ajax({
                                    url : owsrootUrl + L.Util.getParamString(parameters),
                                    dataType : 'jsonp',
                                    jsonpCallback : 'getJson',
                                    success : handleJson
                                    });
                                });
    
    //HERE is where the table is created -  this gets run EVERYTIME there is a seleciton made on the map.
    
                 function handleJson(data) {
                            
                        //////////////////////////////DataTables Stuff///////////////////////////
                            
                            $(document).ready(function() {
                               
                                  var columns = [];
                                  var prop = data.features[0].properties;
                                  for (var i in prop) {
                                      columns.push({data: 'properties.' + i, title: i});
                                  }                             
                                   $('#featureTable').DataTable( {
                                        data: data.features,   //get the data under features
                                        columns: columns,
                                       destroy: true
                                    } );
                                
                                    } );                      
                            ///////////////////////////Feature Data to Point////////////////
                                selectedFeature = L.geoJson(data, {
                                    
                                    onEachFeature: function (feature, layer) {  
                                    },                
                                    pointToLayer: function (feature, latlng) {
                                        
                                        return L.circleMarker(latlng, {
                                            radius: 5,
                                            color: '#3006e8',
                                            weight: 5,
                                            opacity: 100,
                                            fillOpacity: 100
                                        });
                                    }
                                });
                                selectedFeature.addTo(drawnItems);
    

    This all works fine as long as I don't selected a different Layer. If I make a selection in the "Cities" layer and then another in the "Cities" layer, it just creates a new table. BUT, if I change layers to "States" which has a different number of columns with different data in it it won't work. I need to completely remove the first "Cities" table and all associated information. That's why i was clearing and removing everything. However, when I do this and try to make a new selection I get this error:

    Query.Deferred exception: Cannot read property 'properties' of undefined TypeError: Cannot read property 'properties' of undefined
        at HTMLDocument.<anonymous> (file:///H:/EMT07_05_2017/Merged_Code/index.html:727:58)
        at mightThrow (https://code.jquery.com/jquery-3.2.1.js:3583:29)
        at process (https://code.jquery.com/jquery-3.2.1.js:3651:12) undefined
    

    Is there any way around this? So far, removing/destroying "featureTable" and then recreated it in the "handleJson" fucntion everytime has not worked.

  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28
    edited July 2017

    I don't see where .remove() is being called in the code above.
    (you may need to remove the table def from your html so your tableContainer is an empty node):

    So, try changing the lines below

    $('#featureTable').DataTable( {
                                        data: data.features,   //get the data under features
                                        columns: columns,
                                       destroy: true
                                    } );
    
    

    to this:

    // Destroy any old datatable, and remove the table node from the dom
    $('#featureTable').DataTable().destroy(true);
    
    // Create a new table element in the dom and append it to the table container.
    $('#tableContainer').append($('<div/>').attr('id', 'featureTable').css('width', '100%').addClass('display'));
    
    // Turn the new table element into a datatable
    $('#featureTable').DataTable( {
                                        data: data.features,   //get the data under features
                                        columns: columns,
                                       destroy: true
                                    } );
    
  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0
    edited July 2017

    Thanks.

    Sorry, I left out my remove code. I have already tried a combination of the following in~~~~ my clear all button:

                    map.on('draw:deleted', function(){                  
    //                    $("#tableContainer").empty();
    //                    $("#tableContainer").remove();
    //                    $("#featureTable").empty();                   
    //                    $("#featureTable").DataTable().destroy();
    //                    $("#featureTable").remove();
                        });~~~~
    

    So I put that code where you suggested (where the table is first created) and it throws this huge error:

    jQuery.Deferred exception: Cannot read property 'aDataSort' of undefined TypeError: Cannot read property 'aDataSort' of undefined
        at W (https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js:65:443)
        at ya (https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js:70:61)
        at e (https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js:91:115)
        at HTMLTableElement.<anonymous> (https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js:93:118)
        at Function.each (https://code.jquery.com/jquery-3.2.1.js:362:19)
        at jQuery.fn.init.each (https://code.jquery.com/jquery-3.2.1.js:157:17)
        at jQuery.fn.init.m [as dataTable] (https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js:82:462)
        at jQuery.fn.init.h.fn.DataTable (https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js:167:297)
        at HTMLDocument.<anonymous> (file:///H:/EMT07_05_2017/Merged_Code/index.html:685:20)
        at mightThrow (https://code.jquery.com/jquery-3.2.1.js:3583:29) undefined
    
    Uncaught TypeError: Cannot read property 'aDataSort' of undefined
        at W (jquery.dataTables.min.js:65)
        at ya (jquery.dataTables.min.js:70)
        at e (jquery.dataTables.min.js:91)
        at HTMLTableElement.<anonymous> (jquery.dataTables.min.js:93)
        at Function.each (jquery-3.2.1.js:362)
        at jQuery.fn.init.each (jquery-3.2.1.js:157)
        at jQuery.fn.init.m [as dataTable] (jquery.dataTables.min.js:82)
        at jQuery.fn.init.h.fn.DataTable (jquery.dataTables.min.js:167)
        at HTMLDocument.<anonymous> (index.html:685)
        at mightThrow (jquery-3.2.1.js:3583)
    

    I think it was confused trying to delete a table that did not exist when you attempt to build your first table. I then placed this code:

    $('#featureTable').DataTable().destroy(true);
    $('#tableContainer').append($('<div/>').attr('id', 'featureTable').css('width', '100%').addClass('display'));
    

    in in my "map.on('draw:deleted...) code which is basically my "clear all" button. I then get this after I clear and try to create a new table:

    DataTables warning: Non-table node initialisation (DIV). For more information about this error, please see http://datatables.net/tn/2
    

    It acts as if things are not actually being cleared.

  • rduncecbrduncecb Posts: 125Questions: 2Answers: 28

    Oops.

    Should have been:

    $('#tableContainer').append($('<table/>').attr('id', 'featureTable').css('width','100%').addClass('display'));
    
  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0
    edited July 2017

    Hmmm.. whacky. It just does not create a new table now when i place it in the "clear all". No warnings or anything thing logging to the console. Only when I try to place it on the sidebar do I get a warning:

    L.Control.Sidebar.js:175 Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
        at e.setContent (L.Control.Sidebar.js:175)
        at GenerateTable (index.html:187)
        at HTMLButtonElement.onclick (index.html:119)
    

    I've got it right now so that as soon as a table is created the text is just added right on top of the for testing purposes. First table works fine... then I clear.. and I make selections again. No errors, but a new table does not show up. I think that sidebar error "...is not of type Node.." says whatever is actually being created again (if anything) is not a node.

    I could try to put that code right in where the table is actually created, but I would need a way to check if a table already exists and use some logic to it how to proceed.

  • bindridbindrid Posts: 730Questions: 0Answers: 119

    Does your table definition, columns section have the title option set for each column? Datatables has to have that if you are not added <thead><tr> with a <th> for each of your columns.

  • bindridbindrid Posts: 730Questions: 0Answers: 119

    If you are just clearing out the rows but not the header, do your remove on the tbody instead of the table and leaving your headers intact.

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0

    The table are created dynamically each time based on what layers are active. There is the code that determines the column names (the title: i ) part:

                                  for (var i in prop) {
                                      columns.push({data: 'properties.' + i, title: i});
                                      console.log(i);
                                  }  
    

    I believe the destroy and remove commands will clear out everything from the DOM.

  • bindridbindrid Posts: 730Questions: 0Answers: 119
    Answer ✓

    Here is something I created a while ago that uses a select box determine which table to display. It might be helpful

    http://jsbin.com/gumijev/176/edit?html,js,output

  • JeremiahEdwardJeremiahEdward Posts: 25Questions: 4Answers: 0
    edited July 2017

    Oh my god.... all I had to do was add this:

    <thead>
    
    </thead>
    <tfoot>
    
    </tfoot>
    

    I have been staring at this for since yesterday! Everyone on this forum is so great. I would be totally SOL without your help.

    I still get this error about appending the new table to Leaflet Sidebar however. That's another hurdle. There always seems to be one after the next in development....

    L.Control.Sidebar.js:175 Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
        at e.setContent (L.Control.Sidebar.js:175)
        at GenerateTable (index.html:193)
        at HTMLButtonElement.onclick (index.html:125)
    
This discussion has been closed.