Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
217 views
in Technique[技术] by (71.8m points)

javascript - aoData is null when using multiple instances of jquery datatable

Scenario:

On a webpage I have three divs that contain table tags.

There are 3 buttons, clicking on each button creates an instance of the datatable on a particular div with the table tag.

The datatable gets the data from serverside

All the data returned and displayed, pagination, filtering works fine.

So when all three instances are created, using fnSettings() on only the last instance created returns the proper object, and the other two instances return null

So using fnData() etc api methods throw an error saying : "TypeError: Cannot read property 'aoData' of null" because settings object of that datatable instance is somehow null

Code Description

I have made a class called datagrid, and I create multiple instances of this class:

/**
 * datagrid class contains methods and properties that will help in controllling and manipulating the multiple instances of the datagrid class
 * 
 * This function is the constructor for the datagrid class
 * 
 * @param {string} domContainerSelector DOM selector of the element containing the datagrid
 * @param {Array} columns Definitions of the columns of the datagrid
 * @param {string} ajaxSource The url that the jqgrid will use to request for data
 * @param {Object} configurationParameters The configuration parameters that will be used by the jqGrid and this datagrid instance. Currently suppoted configuration parameters are: initialCacheSize, iDisplayLength, sScrollY, bPaginate, bFilter, sDom, bSort
 * @param {Object} uiCallback Contains callback functions that are used when a server request is in progress and after the completion of the request. Mainly used for showing progress indicators.
 * @returns {datagrid}
 */
function datagrid(domContainerSelector, columns, ajaxSource, configurationParameters, uiCallback)
{
    this.domContainerSelector = domContainerSelector;
    this.domTableSelector = this.domContainerSelector + " #grid";
    this.domRowSelector = this.domTableSelector + " tbody tr";
    this.domGridWrapperSelector = this.domContainerSelector + " .dataTables_wrapper";
    this.columns = columns;
    this.ajaxSource = ajaxSource;
    this.configParams = configurationParameters;
    this.uiCallback = uiCallback;
    this.cache= {
            start: 0,
            end: 0,
            initialSize:this.configParams.initialCacheSize == undefined ? 2 : this.configParams.initialCacheSize,
            pageSize:this.configParams.iDisplayLength == undefined ? 10 : this.configParams.iDisplayLength,
            loading:false,
            jsondata: {},
            reset: function(){
                this.start=0;
                this.end=0;
                this.loading=false;
                this.jsondata={};
            }
    };
    /**
     * This method returns the row selected by the user
     * 
     * @return {Object} Row object containing columns as its properties
     */
    this.getSelectedRow = function()
    {
        var allrows = this.dataTable.fnGetNodes();
        for (i = 0; i < allrows.length; i++)
            if ($(allrows[i]).hasClass('row_selected'))
                return this.dataTable.fnGetData(allrows[i]);
    };
    this.getPostDataValue=function(postData, key){
        for (var i=0;i<postData.length;i++)
        {
            if (postData[i].name == key)
            {
                return postData[i].value;
            }
        }
        return null;
    };
    this.setPostDataValue=function(postData, key, value){
        for (var i=0; i<postData.length;i++)
        {
            if (postData[i].name == key)
            {
                postData[i].value = value;
            }
        }
    };
    this.setPostDataFilterValues=function(postData){
        for (i=0;i<this.columns.length;i++)
        {
            var key="sSearch_"+i;
            this.setPostDataValue(postData,key,this.columns[i].sSearch===undefined?'':this.columns[i].sSearch);
        }
    };
    this.filterColumnKeyupHandler = function(evt) {
        var id=evt.target.id;
        var index=id.charAt(id.length-1);
        var oldvalue=this.columns[index].sSearch;
        var value = evt.target.value == '' ? undefined : evt.target.value;
        if (oldvalue!=value) this.cache.reset();//resetting the cache because the datagrid is in dirty state
        this.columns[index].sSearch=value;
        if (evt.keyCode == 13) this.dataTable.fnFilter();
    };
    /**
     * This method acts as the general button handler when an operation is in progress
     */
    this.busyStateButtonHandler=function()
    {
        ui.showmessage("Another operation is in progress. Please wait for the operation to complete");
    };
    /**
     * This method sets the event handlers for the datagrid
    */
    this.setEventHandlers = function() {
        var self=this;
        $(this.domGridWrapperSelector + " input[class='columnfilterinput']").off("keyup").on("keyup", function(evt) {self.filterColumnKeyupHandler(evt,self)});
        $(this.domGridWrapperSelector + " .filterbar .searchbtn").off("click").on("click", function() {self.dataTable.fnFilter()});
    };
    /**
     * This method sets the appropriate event handlers to indicate busy status
    */
    this.setBusyStatusEventHandlers=function()
    {
        $(this.domGridWrapperSelector + " input[class='columnfilterinput']").off("keyup").on("keyup", this.busyStateButtonHandler);
        $(this.domGridWrapperSelector + " .filterbar .searchbtn").off("click").on("click", this.busyStateButtonHandler);
    };
    /**
     * This method enables column specific filtering
     * 
     * This methods adds filtering capability to columns whose definitions indicate that they are searchable (bSearchable:true)
     */
    this.enablecolumnfilter = function() {
        var self = this;
        var oTable = self.dataTable;
        var oSettings = oTable.fnSettings();
        var aoColumns = oSettings.aoColumns;
        var nTHead = oSettings.nTHead;
        var htmlTrTemplate = "<tr class='filterbar'>{content}</tr>";
        var htmlTdTemplate = "<td>{content}</td>";
        var htmlInputTemplate = "<input type='text' name='{name}' id='{id}' class='{class}' /><div class='searchbtn' id='{searchbtnid}'><div class='icon-filter'></div></div>";
        var isAnyColumnFilterable = false;
        var htmlTr = htmlTrTemplate;
        var allHtmlTds = "";
        for (i = 0; i < aoColumns.length; i++)
        {
            var column = aoColumns[i];
            var htmlTd = htmlTdTemplate;
            if (column.bSearchable == true)
            {
                isAnyColumnFilterable = true;
                var htmlInput = htmlInputTemplate;
                htmlInput = htmlInput.replace('{name}', column.mData);
                htmlInput = htmlInput.replace('{id}', "sSearch_" + i);
                htmlInput = htmlInput.replace('{class}', 'columnfilterinput');
                htmlTd = htmlTd.replace('{content}', htmlInput);
            }
            else
                htmlTd = htmlTd.replace('{content}', '');
            allHtmlTds += htmlTd;
        }
        if (isAnyColumnFilterable)
        {
            htmlTr = htmlTr.replace('{content}', allHtmlTds);
            nTHead.innerHTML += htmlTr;
            $(this.domGridWrapperSelector + " .filterbar input[class='columnfilterinput']").each(function(){
                $(this).width($(this).parent().width()-26);
            });
        }
    };
    /**
     * This method enables single selection on the rows of the grid
     */
    this.enableSelection = function()
    {
        $(this.domRowSelector).die("click").live("click", function() {
            if ($(this).hasClass('row_selected')) {
                $(this).removeClass('row_selected');
            }
            else {
                $(this).siblings().removeClass('row_selected');
                $(this).addClass('row_selected');
            }
        });
    };
    this.loadDataIntoCache=function(postData, sourceUrl, start, length){
        if (!this.cache.loading)
        {
            var postData=$.extend(true, [], postData);
            var start = start==undefined?this.cache.end:start;
            var length = length==undefined?this.cache.pageSize:length;
            var end = start + length;
            this.setPostDataValue(postData, "iDisplayStart", start);
            this.setPostDataValue(postData, "iDisplayLength", length);

            var self=this;
            this.cache.loading=true;
            $.ajax({
                type: "POST",
                url: sourceUrl,
                data: postData,
                success:
                        function(json, textStatus, jqXHR)
                        {
                            json = JSON.parse(json);
                            var olddata=self.cache.jsondata.aaData;
                            if (olddata===undefined) self.cache.jsondata = $.extend(true, {}, json);
                            else olddata.push.apply(olddata,json.aaData);
                            self.cache.end=end;
                        },
                error:
                        function(jqXHR, textStatus, errorThrown)
                        {
                            ui.showmessage(jqXHR.responseText);//remove this from here
                        },
                complete:
                        function()
                        {
                            self.cache.loading=false;
                        }
            });
        }
    };
    this.loadDataFromCache=function(postData,sourceUrl){
        var start=this.getPostDataValue(postData, "iDisplayStart");
        var length=this.cache.pageSize;
        var end=start+length;
        var sEcho = this.getPostDataValue(postData,"sEcho");
        if (this.cache.end>=end)
        {
            var jsondata=$.extend(true, {},this.cache.jsondata);
            var data=jsondata.aaData;
            jsondata.aaData=data.splice(start,length);
            jsondata.sEcho = sEcho;
            var totalRecords=jsondata.iTotalRecords;
            if ((this.cache.end-end)<((this.cache.initialSize*this.cache.pageSize)/2) && (totalRecords==0 || this.cache.end<totalRecords) ) this.loadDataIntoCache(postData, sourceUrl);//prefetch data if needed
            return jsondata;
        }
        else
        {
            this.loadDataIntoCache(postData,sourceUrl);
            return null;
        }
    };
    /**
     * This method interfaces with the backend end controller
     * 
     * This method is called when the grid initiates any operation that requires server side processing
     * 
     * @param {String} sSource The source url that will be used for the xhr request
     * @param {Array} aoData Contains the parameters sent by the dataTable that will be forwarded to the backend controller
     * @param {Function} fnCallback The callback function of the dataTable that gets executed to finally render the grid with the data
     */
    this.interfaceWithServer = function(sSource, aoData, fnCallback)
    {
        this.setPostDataFilterValues(aoData);
        var self=this;
        if (this.cache.end==0)
        {
            this.setPostDataValue(aoData, "iDisplayStart", this.cache.start);
            if (this.dataTable!=undefined) this.dataTable.fnSettings()._iDisplayStart=0;
            this.loadDataIntoCache(aoData, sSource, 0, (this.cache.initialSize*this.cache.pageSize));
        }
        var data=this.loadDataFromCache(aoData,sSource);
        if (data!=null) fnCallback(data);
        else
        {
            this.setBusyStatusE

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I believe the problem is that your tables all have the same ID. Please note proper HTML requires unique IDs: http://www.w3.org/TR/html401/struct/global.html#h-7.5.2

id = name [CS]
This attribute assigns a name to an element. This name 
must be unique in a document.

Here are two jsfiddles.
http://jsfiddle.net/QFrz9/

var dt1 = $('#div1 #grid').dataTable();
alert('dt1 settings: ' + dt1.fnSettings());
var dt2 = $('#div2 #grid').dataTable();
alert('dt1 settings: ' + dt1.fnSettings());
alert('dt2 settings: ' + dt2.fnSettings());

http://jsfiddle.net/mRFaP/1/

var dt1 = $('#div1 #grid1').dataTable();
alert('dt1 settings: ' + dt1.fnSettings());
var dt2 = $('#div2 #grid2').dataTable();
alert('dt1 settings: ' + dt1.fnSettings());
alert('dt2 settings: ' + dt2.fnSettings());

The first one duplicates your code, using the same id for the two tables. It displays an alert after the first table is created; fnSettings is not null. Then it displays an alert after the next table is created, and suddenly the fnSettings of table 1 is null. The second jsfiddle uses unique ids, and the problem disappears.

Perhaps your table id could be a combination of the div ID and "grid", e.g., div1grid, div2grid etc. Then you would use domContainerSelector + 'grid' instead of ' #grid'.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...