magento_practitioner
magento_practitioner

Reputation: 161

jQuery table sorter not working for values that are fetched by AJAX

I have implemented jQuery tablesorter in my project.

My table consist of input textfields of which some are populated by ajax. Table sorting is working perfectly for the input fields that are entered by user, but the input fields that are populated from database using ajax are not sorting properly.

My code :

jQuery(function () {
    jQuery('#tablesorter-demo').tablesorter({
        widgets: ['zebra', 'stickyHeaders'],
        headers: {
            2: {
                sorter: 'inputs'
            },
            3: {
                sorter: 'inputs'
            },
            5: {
                sorter: 'inputs'
            }
        }
    });
});

jQuery.tablesorter.addParser({
    id: 'inputs',
    is: function (s) {
        return false;
    },
    format: function (s, table, cell, cellIndex) {
        var jQueryc = jQuery(cell);

        // return 1 for true, 2 for false, so true sorts before false
        if (!jQueryc.hasClass('updateInput')) {
            jQueryc
                .addClass('updateInput')
                .bind('keyup', function () {
                    console.log(table);
                    jQuery(table).trigger('updateCell', [cell, false]); // false to prevent resort
                });
        }
        return jQueryc.find('input[type="text"]').val();
    },
    type: 'text'
});

My AJAX function :

jQuery('.bulkupload').keyup(function () {
    check = 1;
    jQuery("#" + jQuery(this).attr("id")).css("color", "#928F8F");
    var part_no1 = jQuery("#" + jQuery(this).attr("id")).val();
    var fieldcount = part_no1.toString().length;
    var thenum = jQuery(this).attr("id").replace(/^\D+/g, '');

    if (jQuery('#qty' + thenum).val() == '') {
        jQuery('#qty' + thenum).val('Enter Quantity');
        jQuery('#qty' + thenum).css("color", "#DF1F26");
    }

    var url1 = "<?php echo Mage::getBaseUrl(); ?>availableorders/index/getdetails";
    jQuery.ajax({
        type: "POST",
        url: url1,
        data: {
            part_no1: part_no1
        },
        success: function (response) {
            if (response == "check") {
                jQuery('#item_name' + thenum).val("Not Found");
                jQuery('#item_desc' + thenum).val("Not Found");
                jQuery('#av_qty' + thenum).css("color", "#DF1F26");
                jQuery('#item_name' + thenum).css("color", "#DF1F26");
                jQuery('#item_desc' + thenum).css("color", "#DF1F26");
                jQuery('#brand_name' + thenum).css("color", "#DF1F26");
            } 
            else {
                var result1 = jQuery.parseJSON(response);

                jQuery('#item_name' + thenum).val(result1.prodname1);
                jQuery('#item_desc' + thenum).val(result1.productdescription1);
                jQuery('#brand_name' + thenum).val(result1.brand);
                jQuery('#av_qty' + thenum).css("color", "#DF1F26");
                jQuery('#item_name' + thenum).css("color", "#DF1F26");
                jQuery('#item_desc' + thenum).css("color", "#DF1F26");
                jQuery('#brand_name' + thenum).css("color", "#DF1F26");
                if (jQuery('#av_qty' + thenum).val(result1.stock) == 0) {
                    jQuery('#av_qty' + thenum).val("Not in Stock");
                } else {
                    jQuery('#av_qty' + thenum).val(result1.stock);
                }

                jQuery("#tablesorter-demo").trigger('updateCell');
            }
        }
    });
});

Upvotes: 5

Views: 1836

Answers (3)

Mottie
Mottie

Reputation: 86413

From the options & widgets that you are using, it appears that you are actually using my fork of tablesorter instead of the original plugin.

Anyway, the widget you created for the input parser is binding to the cell

jQuery(cell).bind('keyup', function () { ... }

but when the table is sorted, the cells are removed from the tbody causing any event bindings to break. The way to get around this problem is to use delegated event binding on the tbody (but done outside of the widget format function because it only needs to be done once).

jQuery('table tbody').on('keyup', 'input', function(e) {
    var $input = $(e.target);
    ...
}

Additionally, when you update a lot of inputs from your ajax call, it would be better to just update them all at once (.trigger('update')), otherwise you're using the updateCell method too much and likely causing the entire process to take longer than necessary.

This demo uses a very similar method to update checkboxes within a table, so it should be fairly straight-forward to convert it to make it work with input values - if you have trouble, just post here and I'll help.

// checkbox parser
$.tablesorter.addParser( {
    id: 'checkbox',
    is: function( s ) {
        return false;
    },
    format: function( s, table, cell ) {
        var $c = $( cell ).find( 'input' );
        return $c.length ? $c.is( ':checked' ) ? 1 : 2 : s;
    },
    type: 'numeric'
});

$( function() {
    // using .on() which requires jQuery 1.7+
    $( 'table' ).on( 'tablesorter-initialized', function() {

        // class name to add on tr when checkbox is checked
        var highlightClass = 'checked',
        // resort the table after the checkbox is modified?
        resort = true,
        // if a server side database needs to be updated, do it here
        serverCallback = function( table, inputElement ) {},

        $table = $( this ),
        c = this.config,
        wo = c && c.widgetOptions,
        // include sticky header checkbox; if installed
        $sticky = c && wo.$sticky || '',
        doChecky = function( c, col ) {
            $table
                .children( 'tbody' )
                .children( 'tr:visible' )
                .children( 'td:nth-child( ' + ( parseInt( col, 10 ) + 1 ) + ' )' )
                .find( 'input' )
                .each( function() {
                    this.checked = c;
                    $( this ).trigger( 'change' );
                });
        };

        $table
            .children( 'tbody' )
            .on( 'change', 'input', function() {
                // ignore change if updating all rows
                if ( $table[0].ignoreChange ) { return; }
                var col, $this = $( this );
                $this.closest( 'tr' ).toggleClass( highlightClass, this.checked );
                $this.trigger( 'updateCell', [ $this.closest( 'td' ), resort ] );
                // if your server side database needs more parameters, add them here sent to the callback
                serverCallback( $table[0], this );
                // uncheck header if any checkboxes are unchecked
                if ( !this.checked ) {
                    $table.add( $sticky ).find( 'thead input' ).prop( 'checked', false );
                }
            })
            .end()
            .add( $sticky )
            .find( 'thead input' )
            // Click on checkbox in table header to toggle all inputs
            .on( 'change', function() {
                // prevent updateCell for every cell
                $table[0].ignoreChange = true;
                var c = this.checked,
                    col = $( this ).closest( 'th' ).attr( 'data-column' );
                doChecky( c, col );
                // update main & sticky header
                $table.add( $sticky ).find( 'th[data-column=' + col + '] input' ).prop( 'checked', c );
                $table.children( 'tbody' ).children( 'tr:visible' ).toggleClass( highlightClass, c );
                // update all at once
                $table[0].ignoreChange = false;
                $table.trigger( 'update', [ resort ] );
            })
            .on( 'mouseup', function() {
                return false;
            });

    });
});

I should also note that when your ajax call is done and the changes have been applied, that is when an "update" method should be triggered, not "updateCell".

Lastly, there is an existing input-select parser but it doesn't include a method to prevent massive updateCell method calls versus updating the table all at once.

Upvotes: 2

Smokey
Smokey

Reputation: 1897

Try this...

jQuery('.bulkupload').keyup(function () {
    check = 1;
    jQuery("#" + jQuery(this).attr("id")).css("color", "#928F8F");
    var part_no1 = jQuery("#" + jQuery(this).attr("id")).val();
    var fieldcount = part_no1.toString().length;
    var thenum = jQuery(this).attr("id").replace(/^\D+/g, '');

    if (jQuery('#qty' + thenum).val() == '') {
        jQuery('#qty' + thenum).val('Enter Quantity');
        jQuery('#qty' + thenum).css("color", "#DF1F26");
    }

 var url1 = "<?php echo Mage::getBaseUrl(); ?>availableorders/index/getdetails";
    ajaxfunction(url1);
    function ajaxfunction(url1)
    {

        if(window.XMLHttpRequest)
        {
            xmlhttp=new XMLHttpRequest();

        }
        else
        {
            xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }

        xmlhttp.onreadystatechange=function()//callback fn
        {
            if(xmlhttp.readyState==4 && xmlhttp.status==200)
            {
                var response=xmlhttp.responseText;
                if (response == "check") {
                    jQuery('#item_name' + thenum).val("Not Found");
                    jQuery('#item_desc' + thenum).val("Not Found");
                    jQuery('#av_qty' + thenum).css("color", "#DF1F26");
                    jQuery('#item_name' + thenum).css("color", "#DF1F26");
                    jQuery('#item_desc' + thenum).css("color", "#DF1F26");
                    jQuery('#brand_name' + thenum).css("color", "#DF1F26");
                }
                else {
                    var result1 = jQuery.parseJSON(response);

                    jQuery('#item_name' + thenum).val(result1.prodname1);
                    jQuery('#item_desc' + thenum).val(result1.productdescription1);
                    jQuery('#brand_name' + thenum).val(result1.brand);
                    jQuery('#av_qty' + thenum).css("color", "#DF1F26");
                    jQuery('#item_name' + thenum).css("color", "#DF1F26");
                    jQuery('#item_desc' + thenum).css("color", "#DF1F26");
                    jQuery('#brand_name' + thenum).css("color", "#DF1F26");
                    if (jQuery('#av_qty' + thenum).val(result1.stock) == 0) {
                        jQuery('#av_qty' + thenum).val("Not in Stock");
                    } else {
                        jQuery('#av_qty' + thenum).val(result1.stock);
                    }

                    jQuery("#tablesorter-demo").trigger('updateCell');
                    $("#tablesorter-demo").tablesorter();
                }



            }
        }

        xmlhttp.open("GET",url1,true);
        xmlhttp.send();

    }

});

Upvotes: 0

Smokey
Smokey

Reputation: 1897

Table sorting is working perfectly for the input fields that are entered by user, but the input fields that are populated from database using ajax are not sorting properly.

This is because when you load that page, jQuery library & table sorter libraries are loaded at that time and table sorter functionality is added to your table at that time.

tablesorter-demo

Here is the example..

<script type="text/javascript" src="/path/to/jquery-latest.js"></script> 
<script type="text/javascript" src="/path/to/jquery.tablesorter.js"></script> 
$(document).ready(function() 
    { 
        $("#tablesorter-demo").tablesorter(); 
    } 
); 

And when contents are loaded via AJAX, this table sorting property has already been added to the table & it is not applied to the new data fetched by the AJAX function because AJAX function worked after the initial page load. So all you have to do is add the table sorter property

$("#tablesorter-demo").tablesorter();

just above the line

jQuery("#tablesorter-demo").trigger('updateCell');

I hope this helps..because it worked for me when I faced the same problem while loading contents via AJAX & trying to sort 'em.

Upvotes: 0

Related Questions