Alex Barac
Alex Barac

Reputation: 633

Kendo UI and knockout.js rowTemplate sorting

I have a MVC site where I use Kendo UI and knockout.js to display the pages. One scenario is to get the database information from server through $.getJSON and then display this information on a KendoUI grid.

<div data-bind="kendoGrid:{sortable:true, data:users, rowTemplate:'userRowTemplate'}>
    <table>
        <thead>
            <tr>
                <th>Username</th>
                <th>First Name</th>
                <th>Last Name</th>                   
            </tr>
        </thead> </table>
</div>

<script type="text/html">
    <tr>
        <td data-bind="text: Username"></td>
        <td data-bind="text: FirstName"></td>
        <td data-bind="text: LastName"></td>            
    <tr>
</script>

and the javascript :

<script type="text/javascript">
    var ViewModel = function () {
        var self=this;
        self.users=ko.mapping.fromJS([]);
        $getJSON("/UserManagementController/GetUsers",function(data){
            ko.mapping.fromJS(data,{},self.users);
        });                 
    };

    $(document).ready(function(){
        var newViewModel=new ViewModel();
        ko.applyBindings(newViewModel);
    });    
</script>

I want this data to be sortable on specific columns (the ones specified here are for example's sake), but I haven't been able to achieve this successfully. I have tried the solution from this knockout-kendo plugin issue post, which works well on simple objects, but does not work on observables. So my question is : how to map data from database through MVC controller to observables in knockout and displaying them in a Kendo grid, but still be able to sort them?

Thanks, Alex Barac

Upvotes: 0

Views: 1246

Answers (3)

prespic
prespic

Reputation: 1685

DEFINE CUSTOM COMPARE FUNCTION will serves solution, where you can override the comparing function in field definition.

So you can do something like this:

$("#grid").kendoGrid({
    dataSource: dataSource,
    sortable: true,
    columns: [{
        field: "item",
        sortable: {
            compare: function(a, b) {
                var valueA = a["item"];
                var valueB = b["item"];

                if (typeof valueA === "function") valueA = valueA();
                if (typeof valueB === "function") valueB = valueB();

                if (this.isNumeric(valueA) && this.isNumeric(valueB))
                {
                    valueA = parseFloat(valueA);
                    valueB = parseFloat(valueB);
                }

                if (valueA && valueA.getTime && valueB && valueB.getTime)
                {
                    valueA = valueA.getTime();
                    valueB = valueB.getTime();
                }

                if (valueA === valueB)
                {
                    return a.__position - b.__position;
                }

                if (valueA == null)
                {
                    return -1;
                }

                if (valueB == null)
                {
                    return 1;
                }

                if (valueA.localeCompare)
                {
                    return valueA.localeCompare(valueB);
                }

                return valueA > valueB ? 1 : -1;
            }
        }
    }]
});

private isNumeric(input)
    {
        return (input - 0) == input && ('' + input).trim().length > 0;
    }

The compare method is stolen from kendo script, but change is where the property is typeof function (what ko.observable is), it unwraps the value. Plus, I added support for numbers.

Upvotes: 0

wavedrop
wavedrop

Reputation: 530

As an alternative you can modify the kendo.web.js (version 2013.3.1119) on line 6844 & 6844.

Replace

compare: function(field) {
        var selector = this.selector(field);
        return function (a, b) {
                a = selector(a);
                b = selector(b);

With

compare: function(field) {
        var selector = this.selector(field);
        return function (a, b) {
                a = ko.utils.unwrapObservable(selector(a));
                b = ko.utils.unwrapObservable(selector(b));

By using the knockout utility "ko.utils.unwrapObservable" you can get the value of the observable for use when kendo compares the values of the column

Upvotes: 1

Heather Objois
Heather Objois

Reputation: 31

You can do this by creating a JS view model to represent the data coming back from the server, and mapping the data into the view model. Then you can have simple objects that get set by subscribing to the matching observable properties to implement the sort.

Here is an example: http://jsfiddle.net/R4Jys/1/

HTML:

<div data-bind="kendoGrid: gridOptions(myList)"></div>
<script id="rowTmpl" type="text/html">
    <tr>
        <td>
            <span data-bind="text: firstName" />
        </td>
        <td>
            <span data-bind="text: lastName" />
        </td>
        <td>
            <span data-bind="text: userName" />
        </td>
    </tr>
</script>

JavaScript:

var gridItem = function () {
    var self = this;
    self.firstName = ko.observable();
    self.firstNameSort;
    self.firstName.subscribe(function (value) {
        self.firstNameSort = value;
    });
    self.lastName = ko.observable();
    self.lastNameSort;
    self.lastName.subscribe(function (value) {
        self.lastNameSort = value;
    });
    self.userName = ko.observable();
    self.userNameSort;
    self.userName.subscribe(function (value) {
        self.userNameSort = value;
    });
    self.other = ko.observable('test');
    return self;
};
var vm = function() {
    var self = this;
    self.myList = ko.observableArray();
    self.test = ko.observable();
    self.gridOptions = function (data) {
        return {
            data: data,
            rowTemplate: 'rowTmpl',
            useKOTemplates: true,
            scrollable: true,
            sortable: true,
            columns: [
                {
                    field: "firstNameSort",
                    title: "First Name",
                    width: 130
                },
                {
                    field: "lastNameSort",
                    title: "Last Name",
                    filterable: true
                },
                {
                    field: "userNameSort",
                    title: "Username"
                }
            ]
        }
    };
    var data = [{'firstName':'Steve', 'lastName':'Jones', 'userName': 'steve.jones'},
                {'firstName':'Janet', 'lastName':'Smith', 'userName': 'janet.smith'},
                {'firstName':'April', 'lastName':'Baker', 'userName': 'april.baker'},
                {'firstName':'Dave', 'lastName':'Lee', 'userName': 'dave.lee'},
                {'firstName':'Jack', 'lastName':'Bennet', 'userName': 'jack.bennet'},
                {'firstName':'Chris', 'lastName':'Carter', 'userName': 'chris.carter'}];
    self.myList(ko.utils.arrayMap(data, function(item) { 
        var g = new gridItem();
        ko.mapping.fromJS(item, {}, g);
        return g;
    }));
};
var pageVm = new vm();

ko.applyBindings(pageVm);

Upvotes: 2

Related Questions