fooplugins / FooTable

jQuery plugin to make HTML tables responsive
https://fooplugins.com/plugins/footable-jquery/
Other
2.13k stars 637 forks source link

Combining FooTable V3 + pagination with KnockoutJS AJAX calls #392

Closed jausw closed 8 years ago

jausw commented 8 years ago

Hi,

I am using FooTable in conjunction with KnockoutJS. I was using FooTable V2, but the pagination was not fully developed, therefore I am attempting to move to V3. My table rows are defined via an ajax call, with the results populating an observable array in my view model via the KnockoutJS Mapping plugin. The HTML for the table rows is then build in a foreach loop. The table also contains custom search filters, which requery the db via ajax and update the observable array with the results. Due to the structure of the JSON returned, it is not pratical to bind it directly to FooTable using the "rows": JSON_OBJECT syntax.

This combination was working fine with FooTable V2, but I am unable to get the desired results with V3.

Example - HTML table definition:

<table id="referralListTable" class="table footable"   
                        data-sorting="true"
                        data-paging="true"
                        data-paging-size="10" 
                        data-toggle="true"
                        data-toggle-column="first"
                        data-paging-limit="3" 
                        data-cascade="true"
                        >
                    <thead>
                        <tr>
                            <th data-type="html">
                                <img src="icons/calender.png">
                            </th>
                            <th data-sortable="true" data-breakpoints="xs" id="advertiserProductHeader" data-type="html">
                                <img src=icons/product.png">        
                            </th>
                            <th data-sortable="true" data-breakpoints="xs" id="articleHeader" data-type="html" data-visible="${user.shop.showArticleReferral}">
                                <img src="icons/receiver.png">
                            </th>
                            <th data-sortable="true" data-breakpoints="xs" id="recipientHeader" data-type="html" data-visible="${user.shop.showContactInfo}">
                                <img src="icons/awards-receiver.png">
                            </th>
                            <th data-sortable="true" data-breakpoints="xs" data-type="html" >
                                <img src="icons/receiver.png">
                            </th>
                            <th data-sortable="true" data-breakpoints="xs" data-type="html" >
                                <img src="cons/loading.png">
                            </th>
                            <th data-sortable="true" data-breakpoints="all" data-type="html">
                                <img src="icons/awards.png">
                            </th>
                            <th data-sortable="true" data-breakpoints="all" data-type="html" >
                                <img src="icons/awardsloading.png">
                            </th>
                        </tr>   
                    </thead>
                    <tbody data-bind="foreach: viewModel.referrals()">
                        <tr>
                            <td data-bind="text: dateConvertor(created())"></td>
                            <td id="advertiserProductContent">
                                <div id="advertiserName" data-bind="html: advertiser.name"></div>
                                <div id="productName" style="font-weight:bold;" data-bind="html: product.name"></div>
                            </td>
                            <td id="articleName" data-bind="text: article.name"></td>
                            <td id="recipientName" data-bind="text: contactFullName"></td>
                            <td>
                                <table>
                                    <tr data-bind="text: receiver(awardReceiver())"></tr>
                                    <tr>
                                        <td data-bind="foreach: subReferrals()"><div data-bind="text: receiver(awardReceiver())"></div></td>
                                    </tr>
                                </table>                                
                            </td>
                            <td>
                                <table>
                                    <tr data-bind="text: subReferrals() == null ? translatedReferralState(state()) : '&nbsp'"></tr> 
                                    <tr>
                                        <td data-bind="foreach: subReferrals()"><div data-bind="text: translatedReferralState(state())"></div></td>
                                    </tr>
                                </table>    
                            </td>
                            <td>
                                <table>
                                    <tr data-bind="text: subReferrals() == null ? award.name : '&nbsp'"></tr> 
                                    <tr>
                                        <td data-bind="foreach: subReferrals()"><div data-bind="text: award.name"></div></td>
                                    </tr>
                                </table>    
                            </td>
                            <td>
                                 <table>
                                    <tr data-bind="text: subReferrals() == null ? translatedAwardState(awardState()) : '&nbsp'"></tr> 
                                    <tr>
                                        <td data-bind="foreach: subReferrals()"><div data-bind="text: translatedAwardState(awardState())"></div></td>
                                    </tr>
                                </table>    
                            </td>
                        </tr>
                    </tbody>
                </table>

I have run across the following issues.

1: The pagination only counts the rows properly when FooTable is initialized after the KnockoutJS viewmodel binding has been called. Example: Does not work:

$(document).ready(function() {  
    jQuery(function($){
        $('.table').footable({
            "showToggle": true,
            "paging": {
                "enabled": true
              }
        });
    });
    $.getJSON(URL_TO_REST_CALL, function(data) {
        viewModel = ko.mapping.fromJS(data);
        ko.applyBindings(viewModel);
    });
}); 

Does work:

$(document).ready(function() {  
    $.getJSON(URL_TO_REST_CALL, function(data) {
        viewModel = ko.mapping.fromJS(data);
        ko.applyBindings(viewModel);
        jQuery(function($){
            $('.table').footable({
                "showToggle": true,
                "paging": {
                    "enabled": true
                }
            });
        });     
    });
}); 

2: After a "rebinding" of the rows (after a search has been performed using the filters), the new rows are not read by FooTable unless I reinitialize. Essentially the same issue as above. Example: Does not work:

var requeryReferrals = function(){
    ...SET THE FILTER VALUES into the queryString URL...
    viewModel.referrals.removeAll(); 
    $.getJSON(queryString,  function(data) {
        viewModel = ko.mapping.fromJS(data, viewModel);
    }); 
};

Does sort of work:

var requeryReferrals = function(){
    ...SET THE FILTER VALUES into the queryString URL...
    viewModel.referrals.removeAll(); 
    $.getJSON(URL_TO_REST_CALL+queryString, function(data) {
        viewModel = ko.mapping.fromJS(data, viewModel);
        jQuery(function($){
            $('.table').footable({
                "showToggle": true,
                "paging": {
                    "enabled": true
                }
            });
        });
    }); 
};

In the V2 version I triggered the FooTable update (which did work):

var requeryReferrals = function(){
    ...SET THE FILTER VALUES into the queryString URL...
    viewModel.referrals.removeAll(); 
    $.getJSON(URL_TO_REST_CALL+queryString, function(data) {
        viewModel = ko.mapping.fromJS(data, viewModel);
        $('.table').trigger('footable_redraw'); 
    }); 
};

3: After rebinding of my viewmodel with the new dataset, from within FooTable, I get the following javascript error: FooTable: unhandled error thrown while executing "draw".

TypeError: b[0] is undefined
...{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string...
footable.js (line 2005)

It is as if there is an empty row somewhere.

In this scenario, the table shows the correct number of rows, but the pagination is incorrect (present when it should not be, not displayed correctly). With debug activated, I can see that FooTable completes the event FooTable:predraw.ft.table and then throws the error in FooTable:draw.ft.table

When I then resize the browser, the orginal set of rows is displayed (from the initial page load).

Now, I realize that in the callback function, where the filtered dataset is returned, reinitializing FooTable is not the proper solution. This is where, in V2, I was simply triggering the footable_redraw event. I have tried calling destroy() first, but the results were the same. I attempted triggering the "resize.ft.table" event, but that seemed to do nothing.

Nice would be an update() or refresh() function, which would remove all rows and reset them with the dynamic data.

Here are some screenshots of what I am describing: 1_initial-page-load-pagination-working 2_after-search-pagination-messed-up 3_after-browser-resize-previous-resultset-shown-pagination-messed-up

iKonrad commented 8 years ago

have you managed to fix it? I'm having the same issue even when copypasting the example from the page.

jausw commented 8 years ago

Hi,

No, I have not fixed it. I simply started using the footable filter and removed my custom filters. That way, I don't have the rerender issues I was describing.

On 15.03.2016 23:02, Konrad wrote:

have you managed to fix it? I'm having the same issue even when copypasting the example from the page.

— You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub https://github.com/fooplugins/FooTable/issues/392#issuecomment-197045186

steveush commented 8 years ago

Hi,

Apologies I don't use KnockoutJS and I'm not sure why you would have been encountering this issue. I have added in a method on the FooTable.Rows object called load(Array.<object>, bool) to make loading a new JSON array of rows a lot simpler. This array follows the same structure as if you were using the rows option. The second parameter basically tells FooTable to either dump the current rows and replace them with the new ones or to append them instead to the current rows array. There is also a shortcut method available on the FooTable.Table object called loadRows(Array.<object>, bool).

As an example you can use it like below:

FooTable.get('#table').loadRows([{id: 1, ...}, {id: 2, ...}, {id: 3, ...}]);

This will be available in the next version of the plugin being pushed shortly. I'll be closing this issue as I don't intend to fix it as I don't use KnockoutJS. If you figure out a work around using the new functions please let me know and I'll implement it.

Thanks