Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery tablesorter with knockout - adding rows

I am trying to use tablesorter with the savesort widget. The table is populated by knokout.

The first time the table is populated, i get the correct amount of rows, and it remembers sorting. I then repopulate the table with data, using ajax and binding to the knockout array. The data is then doubled, but knockout (correctly), only tracks half the data.

So far i have tracked it down to when i apply the saved sorting in this method:

self.tablesorter = function () {
                        $('table.tablesorterTranslation').trigger("update");
                        setTimeout(function () {
                            var sl = $.tablesorter.storage($('table.tablesorterTranslation'), 'tablesorter-savesort');
                            var sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : '';
                            if (sortList && sortList.length > 0) {
                                // update sort change
                                $('table.tablesorterTranslation').trigger("sorton", [sortList]);
                            }
                        }, 1000);
                        return false;
                    };

This method is called by knockouts postaction binding.

Before the method is called, i have one tablerow for every item in the knockout array, after postaction, i have two, one tracked by knockout, and one that seems to be added by tablesorter.

Is this due to tablesorter caching values, and is there a way to clean it?

The html:

    <table class="tablesorterTranslation" data-bind="visible: contents().length > 0">
                <colgroup>
                    <col class="referencenumber"/>
                    <col class="title"/>
                </colgroup>
                <thead>
                    <tr>
                        <th class="referencenumber">Ref number</th>
                        <th class="title">Title</th>
                    </tr>
                </thead>
                <tbody data-bind="foreach: contents, postAction: tablesorter(), visible: contents().length > 0">
                    <tr>
                        <td class="referencenumber">
                            <span data-bind="text: contentReferenceNumber"></span>
                        </td>
                        <td class="title">
                            <a data-bind="attr: {href: previewUrl, title: previewTitle}, click: previewpopup" class="preview_translation"><%: AdminResources.Menu_Preview %></a>
                      </td>
                </tr>
            </tbody>
        </table>

The ajax that repopulates the table:

self.setContentList = function () {
                        if ($('#LanguageIdNameValuePairs option').length > 0) {
                            self.contents.removeAll();
                            self.loading(true);
                            $.ajax('<%= Url.Action("GetContentList", "TranslateContentMenu") %>',
                                {
                                    dataType: 'json',
                                    data: {
                                        languageId: $('#LanguageIdNameValuePairs').val(),
                                        page: self.page
                                    },
                                    success: function (allData) {
                                        var mappedContent = $.map(allData, function (item) { return new ContentViewModel(item); });
                                        self.loading(false);
                                        self.contents(mappedContent);
                                    }
                                });
                        } else {
                            self.contents(new Array());
                        }
                    };

The contentviewmodel:

(function (ko) {
    PODIUM.translation.ContentViewModel = function(data) {
        this.contentReferenceId = ko.observable(data.contentReferenceId);
        this.contentName = data.contentName;
        this.previewUrl = ko.observable(data.previewUrl);
        this.previewTitle = ko.observable(data.previewTitle);
        this.publishTitle = ko.observable(data.publishTitle);
        this.contentReferenceNumber = ko.observable(data.contentReferenceNumber);
    };
}(ko));

and last, populating the table and defining tablesort, with savesort (savesort is the issue i think).

       $('#OptionsMenu').change(function () {
            viewModel.setContentList();
        });
        $('table.tablesorterTranslation').tablesorter({
            widgets: ["saveSort"],
            widgetOptions: {}
        });

I have a select list, with some values, when this changes, i get new values from the server and repopulate the table. This is where it remembers old values, but i want to "clean" the table and start over.

like image 922
ruffen Avatar asked Nov 03 '22 07:11

ruffen


1 Answers

I found an answer myself, but not the one I wanted.

Basically, since I am using knockout, I am not supposed to do changes to the view itself, but rather change the viewmodel. So, i had a look at sorting the arrays, and letting knockout do the rest of the magic.

I started by removing all traces of tablesorter (except for the css class on the table, since i was using this in the stylesheet already).

Then i added this method:

            self.sortContents = function (element, sortProperty) {
                var elem = $(element);
                var direction = (elem.hasClass('headerSortDown')) ? 'headerSortUp' : 'headerSortDown';
                var selector = $.trim($(element).attr('class').replace('header', '').replace('headerSortUp', '').replace('headerSortDown', ''));
                if (direction == 'headerSortUp') {
                    self.filterStorage.updateSortFilter(selector, sortProperty);
                    self.contents.sort(function(left, right) {
                        return left[sortProperty] == right[sortProperty] ? 0 : (left[sortProperty] > right[sortProperty] ? -1 : 1);
                    });
                    elem.removeClass('headerSortDown');
                    elem.addClass('headerSortUp');
                } else {
                    self.filterStorage.updateSortFilter(selector, sortProperty);
                    self.contents.sort(function (left, right) {
                        return left[sortProperty] == right[sortProperty] ? 0 : (left[sortProperty] < right[sortProperty] ? -1 : 1);
                    });
                    elem.removeClass('headerSortUp');
                    elem.addClass('headerSortDown');
                }
            };

Notice im using the same css classes as tablesort plugin, this is because tablesorter is already used in other places in the application, and the css is already there for those classes.

i changed the table header to this:

<th class="title header" data-bind="click: function(data, event){ return sortContents($(event.target), 'contentName')}"><%: AdminResources.ContentOverview_Title %></th>

Then, since I got up in this mess, trying to save the sort state, i had to figure out something for this. So i created a new class with to methods, update, and get. That saves which header im sorting with, and the direction.

function getSortFilter() {
    var cookie = $.cookie(tableSortedByCookie);
    return JSON.parse(cookie);
}
function updateSortFilter(selector, property) {
    $.cookie(tableSortedByCookie, null);
    $.cookie(tableSortedByCookie, JSON.stringify({ selector: selector, property: property }), { path: '/' });
}

You can see the usage of update, in sortcontents method. (this method is on the viewmodel for the page containing the table).

I then added this to the bottom of the ajax success method:

var tableSortCookie = self.filterStorage.getSortFilter();
if (tableSortCookie != null && tableSortCookie.selector != null) {
   var header = $('.tablesorterTranslation th.' + tableSortCookie.selector);
   self.sortContents(header, tableSortCookie.property);
}

And then, I was able to sort the data using knockout instead, as well as storing the state and loading the state back when changing data, and refreshing the page.

like image 190
ruffen Avatar answered Nov 09 '22 12:11

ruffen