Knockout js - сортировка таблицы с использованием заголовков столбцов

Я новичок в knockout js и нуждаюсь в тренировке помощи, чтобы динамически сортировать таблицу, используя заголовок столбца.

Ниже приведена часть моего кода:

HTML:

    <table>


<thead>
                <tr data-bind="click: sortFunction">
                    <th id='id'>Id</th>
                    <th id='name'>Name</th>
                    <th id='description'>Description</th>
                </tr>
            </thead>

                <tbody data-bind="foreach: deptList">
                    <tr>
                            <td><span data-bind="text: id" /></td>
                            <td><span data-bind="text: name" /></td>
                            <td><span data-bind="text: description" /></td>
                    </tr>    
                </tbody>
            </table>

В моей модели модели у меня есть следующая функция, которую я использую для сортировки таблицы данных с использованием заголовка таблицы.

ViewModel:

self.deptList = ko.observableArray(mylist);
self.sortColumn = ko.observable("id");
self.isSortAsc = ko.observable("True");

var Dept = function (id, name, description) {
            this.id = ko.observable(id);
            this.name = ko.observable(name);
            this.description = ko.observable(description);
           };

var mylist = [
            new Dept(1, "Dept 1", "D1"),
            new Dept(2, "Dept 2", "D6"),
            new Dept(3, "Dept 3", "D3"),
            new Dept(4, "Dept 4", "D4")];


self.sortFunction = function (data, event) {

                if(self.sortColum === event.target.id)
                    self.isSortAsc = !self.isSortAsc;
                else
                {
                    self.sortColumn = event.target.id;
                    self.isSortAsc = "True";
                }               


                self.deptList.sort(function (a, b) {
                      if(self.sortColum === 'id')
                      {
                           if(self.isSortAsc)
                              a.id < b.id ? -1 : 1;
                           else
                              a.name < b.name ? 1 : -1;
                      }
                      else if(self.sortColum === 'name'){
                           if(self.isSortAsc)
                              a.name < b.name ? -1 : 1;
                           else
                              a.name < b.name ? 1 : -1;
                      }
                      else (self.sortColum === 'description'){
                           if(self.isSortAsc)
                              a.description < b.description ? -1 : 1;
                           else
                              a.description < b.description ? 1 : -1;
                      }

                });

            };

Несмотря на то, что над кодом работает, я думаю, что должен быть лучший способ сделать это (я имею в виду передачу идентификатора столбца в качестве параметра), который будет полезен, когда имеется большое количество столбцов.

Я попробовал: left [self.sortColumn] < right [self.sortColumn]? -1:1 который не функционировал должным образом.

Если можно отсортировать имя динамического столбца, пожалуйста, укажите пример кода. Спасибо заранее.

Ответ 1

Вот быстрый bindHandler, который вы можете добавить в свой. После нажатия кнопки th он сортирует столбец по имени свойства, как определено в привязке.

ko.bindingHandlers.sort = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var asc = false;
        element.style.cursor = 'pointer';

        element.onclick = function(){
            var value = valueAccessor();
            var prop = value.prop;
            var data = value.arr;

            asc = !asc;

            data.sort(function(left, right){
                var rec1 = left;
                var rec2 = right;

                if(!asc) {
                    rec1 = right;
                    rec2 = left;
                }

                var props = prop.split('.');
                for(var i in props){
                    var propName = props[i];
                    var parenIndex = propName.indexOf('()');
                    if(parenIndex > 0){
                        propName = propName.substring(0, parenIndex);
                        rec1 = rec1[propName]();
                        rec2 = rec2[propName]();
                    } else {
                        rec1 = rec1[propName];
                        rec2 = rec2[propName];
                    }
                }

                return rec1 == rec2 ? 0 : rec1 < rec2 ? -1 : 1;
            });
        };
    }
    };

Это немного более элегантно, поскольку вам не нужно создавать отдельную функцию для каждого столбца. Более полный рабочий пример можно найти здесь: http://jsfiddle.net/brendonparker/6S85t/

Ответ 2

left[self.sortColumn] < right[self.sortColumn] ? -1 : 1

не работает, потому что self.sortColumn является ko.observable, на котором вы должны получить значение, вызвав его как функцию.

self.deptList.sort(function (a, b) {
  if(self.isSortAsc())
     a[self.sortColumn()]() < b[self.sortColumn()]() ? -1 : 1;
  else
     a[self.sortColumn()]() < b[self.sortColumn()]() ? 1 : -1;
});

а также вам нужно установить значения для наблюдаемых, например

if(self.sortColum === event.target.id)
                    self.isSortAsc(!self.isSortAsc());
                else
                {
                    self.sortColumn(event.target.id);
                    self.isSortAsc("True");
                }

вместо

if(self.sortColum === event.target.id)
                    self.isSortAsc = !self.isSortAsc;
                else
                {
                    self.sortColumn = event.target.id;
                    self.isSortAsc = "True";
                } 

Я собрал рабочий пример

<table class="dept_table">
  <thead>
      <tr data-bind="click: sortFunction">
          <th id='id'>Id</th>
          <th id='name'>Name</th>
          <th id='description'>Description</th>
      </tr>
  </thead>

  <tbody data-bind="foreach: deptList">
      <tr>
              <td><span data-bind="text: id" /></td>
              <td><span data-bind="text: name" /></td>
              <td><span data-bind="text: description" /></td>
      </tr>    
  </tbody>
</table>
<script>
// deptlist data
var mylist = [
            {id:1, name:"Dept 1", description: "D1"},
            {id:2, name:"Dept 2", description: "D6"},
            {id:3, name:"Dept 3", description: "D3"},
            {id:4, name:"Dept 4", description: "D4"}];

// Deptlist-item Viewmodel
var Dept = function (data) {
  var self = this;

  for(var key in data){
    // this is the lazy approach usually you should only use observables where they are needed
    if(data.hasOwnProperty(key))this[key] = ko.observable(data[key]);
  }
};

// Deptlist Viewmodel
var Deptlist = function(table_data){
  var self = this;

  this.deptList = ko.observableArray([]);
  this.sortColumn = ko.observable("id");
  this.isSortAsc = ko.observable(true);

  for(var i = 0;i < table_data.length;i++){
    if(table_data.hasOwnProperty(i))this.deptList.push(new Dept(table_data[i]));
  }

  this.sortFunction = function(){
    if(self.sortColumn() === event.target.id)
        self.isSortAsc(!self.isSortAsc());
    else
    {
        self.sortColumn(event.target.id);
        self.isSortAsc(true);
    }

    self.deptList.sort(function (a, b) {
            if(a[self.sortColumn()]() < b[self.sortColumn()]())return !self.isSortAsc();
            else return self.isSortAsc();
    });
  }
};

var deptList = new Deptlist(mylist);

ko.applyBindings(deptList,$('.dept_table')[0]);

</script>