Knockout.js:如何排序table?
Knockout.js: How to sort table?
我用这个例子:
Table 显示可观察数组。该数组由客户端通过 signalr 更新。一切正常。
我想添加按选定列排序。
<div id="computerInfo">
<h2>Real-time System Resource Monitor</h2>
<h5 data-bind="visible: connected">Connected to message hub</h5>
<table border="0" class="table table-striped">
<tr>
<th>Machine</th>
<th>CPU %</th>
<th>Memory Available (Mb)</th>
<th>Total Memory (Mb)</th>
<th>Mem Available %</th>
</tr>
<!-- ko foreach: machines -->
<tr data-bind="css: { highCpu: cpu() > 90 || memPercent()<30 }">
<td data-bind="text: machineName"></td>
<td data-bind="text: cpu"></td>
<td data-bind="text: memUsage"></td>
<td data-bind="text: memTotal"></td>
<td data-bind="text: memPercent"></td>
</tr>
<!-- /ko -->
</table>
$(function () {
// The view model that is bound to our view
var ViewModel = function () {
var self = this;
// Whether we're connected or not
self.connected = ko.observable(false);
// Collection of machines that are connected
self.machines = ko.observableArray();
self.headers = [
{ title: 'Machine Name', sortKey: 'keyMachineName' },
{ title: 'CPU', sortKey: 'keyCpu' },
{ title: 'Mem Usage', sortKey: 'keyMemUsage' },
{ title: 'Mem Total', sortKey: 'keyMemTotal' },
{ title: 'Mem Percent', sortKey: 'keyMemPercent' }
];
self.sort = function (header, event) {
var sortKey = header.sortKey;
//implementation of sort
};
self.sort = function (header, event) {
var sortKey = header.sortKey;
switch (sortKey) {
case 'keyMachineName':
self.machines.sort(function (a, b) {
var n = a.machineName < b.machineName ? -1 : a.machineName > b.machineName ? 1 : 0;
alert(n);
return n;//1;
});
break;
case 'keyCpu':
self.machines.sort(function (a, b) {
var n = a.cpu < b.cpu ? -1 : a.cpu > b.cpu ? 1 : 0;
alert(n);
return n;
});
break;
case 'keyMemUsage':
self.machines.sort(function (a, b) {
var n = a.memUsage < b.memUsage ? -1 : a.memUsage > b.memUsage ? 1 : 0;
alert(n);
return n;
});
break;
}
};
};
// Instantiate the viewmodel..
var vm = new ViewModel();
// .. and bind it to the view
//ko.applyBindings(vm, $("#computerInfo")[0]);
// Get a reference to our hub
var hub = $.connection.cpuInfo;
// Add a handler to receive updates from the server
hub.client.cpuInfoMessage = function (machineName, cpu, memUsage, memTotal) {
var machine = {
machineName: machineName,
cpu: cpu.toFixed(0),
memUsage: (memUsage / 1024).toFixed(2),
memTotal: (memTotal / 1024).toFixed(2),
memPercent: ((memUsage / memTotal) * 100).toFixed(1) + "%"
};
var machineModel = ko.mapping.fromJS(machine);
// Check if we already have it:
var match = ko.utils.arrayFirst(vm.machines(), function (item) {
return item.machineName() == machineName;
});
if (!match) {
vm.machines.push(machineModel);
} else {
var index = vm.machines.indexOf(match);
vm.machines.replace(vm.machines()[index], machineModel);
}
// vm.machines.sort();
//ko.applyBindings(vm, $("#computerInfo")[0]);
//$("#infoTable").tablesorter({sortList: [0,0]});
};
///
ko.applyBindings(vm, $("#computerInfo")[0]);
///
// Start the connectio
$.connection.hub.start().done(function () {
vm.connected(true);
});
});
问题是:
排序函数总是 returns 0,即值总是相同的:
var n = a.machineName < b.machineName
当我进行测试时 return 1 然后排序工作正常。
当您使用
创建机器时
var machineModel = ko.mapping.fromJS(machine);
您创建了一个具有可观察属性的视图模型。在这种情况下,在您的排序方法中,您需要将它们视为可观察对象,因此您必须使用括号来使用它们。例如:
case 'keyMachineName':
self.machines.sort(function (a, b) {
var n = a.machineName() < b.machineName() ? -1 : a.machineName() > b.machineName() ? 1 : 0;
alert(n);
return n;//1;
});
break;
jQuery tablesorter 插件不适用于 knockout。干脆算了。
当 DOM 节点在其不知情的情况下四处移动时,Knockout 无法处理,但这正是 jQuery Tablesorter 所做的。
但是 Knockout 已经做好了为你排序 table 的准备。
以下示例功能
- 完全可配置的列,即未在视图中进行硬编码
单击 - sortable table 列,包括已排序列的视觉反馈
- 交替 ascending/descending 排序
- 通过 Knockout extender
格式化值
- 在一个时间间隔内自动更新值(此用例中的可能场景)
- 值更新时自动重新排序
ko.extenders.formatted = function (underlyingObservable, formatFunction) {
underlyingObservable.formatted = ko.computed(function () {
return formatFunction(underlyingObservable());
});
};
function Machine(data) {
var self = this,
memoryFormat = function (val) { return Math.round(val / 1024, 0).toLocaleString(); };
self.machineName = ko.observable().extend({formatted: function (val) { return val; }});
self.cpu = ko.observable(0).extend({formatted: function (val) { return val.toFixed(1); }});
self.memUsage = ko.observable(0).extend({formatted: memoryFormat});
self.memTotal = ko.observable(0).extend({formatted: memoryFormat});
self.memPercent = ko.computed(function () {
return self.memUsage() / self.memTotal() * 100;
}).extend({formatted: function (val) { return val.toFixed(1) + '%'; }});
self.conditions = ko.computed(function () {
return {
highCpu: self.cpu() > 50,
lowMem: self.memPercent() < 30
};
});
self.update(data);
};
Machine.prototype.update = function (data) {
ko.mapping.fromJS(data, {}, this);
};
function MachineList() {
var self = this;
self.connected = ko.observable(false);
self.machines = ko.observableArray();
self.headers = [
{title: 'Machine Name', key: 'machineName', cssClass: ''},
{title: 'CPU %', key: 'cpu', cssClass: 'right'},
{title: 'Mem Usage (MB)', key: 'memUsage', cssClass: 'right'},
{title: 'Mem Total (MB)', key: 'memTotal', cssClass: 'right'},
{title: 'Mem Available %', key: 'memPercent', cssClass: 'right'}
];
self.sortHeader = ko.observable(self.headers[0]);
self.sortDirection = ko.observable(1);
self.toggleSort = function (header) {
if (header === self.sortHeader()) {
self.sortDirection(self.sortDirection() * -1);
} else {
self.sortHeader(header);
self.sortDirection(1);
}
};
// use a computed to subscribe to both self.sortHeader() and self.sortDirection()
self.sortMachines = ko.computed(function () {
var sortHeader = self.sortHeader(),
dir = self.sortDirection(),
tempMachines = self.machines(),
prop = sortHeader ? sortHeader.key : "";
if (!prop) return;
tempMachines.sort(function (a, b) {
var va = ko.unwrap(a[prop]),
vb = ko.unwrap(b[prop]);
return va < vb ? -dir : va > vb ? dir : 0;
});
self.machines.notifySubscribers();
});
self.insertMachine = function (data) {
var machine = ko.utils.arrayFirst(vm.machines(), function (item) {
return ko.unwrap(item.machineName) == ko.unwrap(data.machineName);
});
if (machine) machine.update(data);
else vm.machines.push(new Machine(data));
self.sortMachines();
};
};
// -------------------------------------------------------------------------------
// connection mockup
$.connection = {
cpuInfo: {client: {}},
hub: {start: function() { return $.Deferred().resolve(); }}
};
var vm = new MachineList(),
hub = $.connection.cpuInfo;
hub.client.cpuInfoMessage = function (data) { vm.insertMachine(data); };
// start the connection as soon as possible...
$.connection.hub.start().done(function () {
vm.connected(true);
$(function () {
// ...but don't apply bindings before the document is ready
ko.applyBindings(vm, $("#computerInfo")[0]);
});
// create some random sample data
setInterval(function () {
var GB = 1024 * 1024;
['A', 'B', 'C', 'D'].forEach(function (name) {
hub.client.cpuInfoMessage({
machineName: name,
cpu: getRandomInt(0, 1000) / 10,
memUsage: getRandomInt(5 * GB, 7 * GB),
memTotal: 10 * GB
});
});
}, 1000);
});
// -------------------------------------------------------------------------------
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
.table { border-collapse: collapse; }
.table th, .table td { border: 1px solid silver; padding: 0 2px; }
.sortable { cursor: pointer; }
.table-striped tbody tr:nth-child(odd) { background-color: #f7f7f7; }
.highCpu { color: red; }
.lowMem { color: red; }
.right { text-align: right; }
.sorted { background-color: #B5E0FF; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div id="computerInfo">
<h2>Real-time System Resource Monitor</h2>
<h5 data-bind="visible: connected">Connected to message hub</h5>
<table border="0" class="table table-striped">
<thead>
<tr data-bind="foreach: headers">
<th class="sortable" data-bind="click: $root.toggleSort, text: title, css: {sorted: $data === $root.sortHeader()}"></th>
</tr>
</thead>
<tbody data-bind="foreach: machines">
<tr data-bind="foreach: $root.headers, css: conditions">
<td data-bind="text: $parent[key].formatted, css: cssClass"></td>
</tr>
</tbody>
</table>
</div>
我用这个例子:
Table 显示可观察数组。该数组由客户端通过 signalr 更新。一切正常。 我想添加按选定列排序。
<div id="computerInfo">
<h2>Real-time System Resource Monitor</h2>
<h5 data-bind="visible: connected">Connected to message hub</h5>
<table border="0" class="table table-striped">
<tr>
<th>Machine</th>
<th>CPU %</th>
<th>Memory Available (Mb)</th>
<th>Total Memory (Mb)</th>
<th>Mem Available %</th>
</tr>
<!-- ko foreach: machines -->
<tr data-bind="css: { highCpu: cpu() > 90 || memPercent()<30 }">
<td data-bind="text: machineName"></td>
<td data-bind="text: cpu"></td>
<td data-bind="text: memUsage"></td>
<td data-bind="text: memTotal"></td>
<td data-bind="text: memPercent"></td>
</tr>
<!-- /ko -->
</table>
$(function () {
// The view model that is bound to our view
var ViewModel = function () {
var self = this;
// Whether we're connected or not
self.connected = ko.observable(false);
// Collection of machines that are connected
self.machines = ko.observableArray();
self.headers = [
{ title: 'Machine Name', sortKey: 'keyMachineName' },
{ title: 'CPU', sortKey: 'keyCpu' },
{ title: 'Mem Usage', sortKey: 'keyMemUsage' },
{ title: 'Mem Total', sortKey: 'keyMemTotal' },
{ title: 'Mem Percent', sortKey: 'keyMemPercent' }
];
self.sort = function (header, event) {
var sortKey = header.sortKey;
//implementation of sort
};
self.sort = function (header, event) {
var sortKey = header.sortKey;
switch (sortKey) {
case 'keyMachineName':
self.machines.sort(function (a, b) {
var n = a.machineName < b.machineName ? -1 : a.machineName > b.machineName ? 1 : 0;
alert(n);
return n;//1;
});
break;
case 'keyCpu':
self.machines.sort(function (a, b) {
var n = a.cpu < b.cpu ? -1 : a.cpu > b.cpu ? 1 : 0;
alert(n);
return n;
});
break;
case 'keyMemUsage':
self.machines.sort(function (a, b) {
var n = a.memUsage < b.memUsage ? -1 : a.memUsage > b.memUsage ? 1 : 0;
alert(n);
return n;
});
break;
}
};
};
// Instantiate the viewmodel..
var vm = new ViewModel();
// .. and bind it to the view
//ko.applyBindings(vm, $("#computerInfo")[0]);
// Get a reference to our hub
var hub = $.connection.cpuInfo;
// Add a handler to receive updates from the server
hub.client.cpuInfoMessage = function (machineName, cpu, memUsage, memTotal) {
var machine = {
machineName: machineName,
cpu: cpu.toFixed(0),
memUsage: (memUsage / 1024).toFixed(2),
memTotal: (memTotal / 1024).toFixed(2),
memPercent: ((memUsage / memTotal) * 100).toFixed(1) + "%"
};
var machineModel = ko.mapping.fromJS(machine);
// Check if we already have it:
var match = ko.utils.arrayFirst(vm.machines(), function (item) {
return item.machineName() == machineName;
});
if (!match) {
vm.machines.push(machineModel);
} else {
var index = vm.machines.indexOf(match);
vm.machines.replace(vm.machines()[index], machineModel);
}
// vm.machines.sort();
//ko.applyBindings(vm, $("#computerInfo")[0]);
//$("#infoTable").tablesorter({sortList: [0,0]});
};
///
ko.applyBindings(vm, $("#computerInfo")[0]);
///
// Start the connectio
$.connection.hub.start().done(function () {
vm.connected(true);
});
});
问题是: 排序函数总是 returns 0,即值总是相同的:
var n = a.machineName < b.machineName
当我进行测试时 return 1 然后排序工作正常。
当您使用
创建机器时var machineModel = ko.mapping.fromJS(machine);
您创建了一个具有可观察属性的视图模型。在这种情况下,在您的排序方法中,您需要将它们视为可观察对象,因此您必须使用括号来使用它们。例如:
case 'keyMachineName':
self.machines.sort(function (a, b) {
var n = a.machineName() < b.machineName() ? -1 : a.machineName() > b.machineName() ? 1 : 0;
alert(n);
return n;//1;
});
break;
jQuery tablesorter 插件不适用于 knockout。干脆算了。
当 DOM 节点在其不知情的情况下四处移动时,Knockout 无法处理,但这正是 jQuery Tablesorter 所做的。
但是 Knockout 已经做好了为你排序 table 的准备。
以下示例功能
- 完全可配置的列,即未在视图中进行硬编码 单击
- sortable table 列,包括已排序列的视觉反馈
- 交替 ascending/descending 排序
- 通过 Knockout extender 格式化值
- 在一个时间间隔内自动更新值(此用例中的可能场景)
- 值更新时自动重新排序
ko.extenders.formatted = function (underlyingObservable, formatFunction) {
underlyingObservable.formatted = ko.computed(function () {
return formatFunction(underlyingObservable());
});
};
function Machine(data) {
var self = this,
memoryFormat = function (val) { return Math.round(val / 1024, 0).toLocaleString(); };
self.machineName = ko.observable().extend({formatted: function (val) { return val; }});
self.cpu = ko.observable(0).extend({formatted: function (val) { return val.toFixed(1); }});
self.memUsage = ko.observable(0).extend({formatted: memoryFormat});
self.memTotal = ko.observable(0).extend({formatted: memoryFormat});
self.memPercent = ko.computed(function () {
return self.memUsage() / self.memTotal() * 100;
}).extend({formatted: function (val) { return val.toFixed(1) + '%'; }});
self.conditions = ko.computed(function () {
return {
highCpu: self.cpu() > 50,
lowMem: self.memPercent() < 30
};
});
self.update(data);
};
Machine.prototype.update = function (data) {
ko.mapping.fromJS(data, {}, this);
};
function MachineList() {
var self = this;
self.connected = ko.observable(false);
self.machines = ko.observableArray();
self.headers = [
{title: 'Machine Name', key: 'machineName', cssClass: ''},
{title: 'CPU %', key: 'cpu', cssClass: 'right'},
{title: 'Mem Usage (MB)', key: 'memUsage', cssClass: 'right'},
{title: 'Mem Total (MB)', key: 'memTotal', cssClass: 'right'},
{title: 'Mem Available %', key: 'memPercent', cssClass: 'right'}
];
self.sortHeader = ko.observable(self.headers[0]);
self.sortDirection = ko.observable(1);
self.toggleSort = function (header) {
if (header === self.sortHeader()) {
self.sortDirection(self.sortDirection() * -1);
} else {
self.sortHeader(header);
self.sortDirection(1);
}
};
// use a computed to subscribe to both self.sortHeader() and self.sortDirection()
self.sortMachines = ko.computed(function () {
var sortHeader = self.sortHeader(),
dir = self.sortDirection(),
tempMachines = self.machines(),
prop = sortHeader ? sortHeader.key : "";
if (!prop) return;
tempMachines.sort(function (a, b) {
var va = ko.unwrap(a[prop]),
vb = ko.unwrap(b[prop]);
return va < vb ? -dir : va > vb ? dir : 0;
});
self.machines.notifySubscribers();
});
self.insertMachine = function (data) {
var machine = ko.utils.arrayFirst(vm.machines(), function (item) {
return ko.unwrap(item.machineName) == ko.unwrap(data.machineName);
});
if (machine) machine.update(data);
else vm.machines.push(new Machine(data));
self.sortMachines();
};
};
// -------------------------------------------------------------------------------
// connection mockup
$.connection = {
cpuInfo: {client: {}},
hub: {start: function() { return $.Deferred().resolve(); }}
};
var vm = new MachineList(),
hub = $.connection.cpuInfo;
hub.client.cpuInfoMessage = function (data) { vm.insertMachine(data); };
// start the connection as soon as possible...
$.connection.hub.start().done(function () {
vm.connected(true);
$(function () {
// ...but don't apply bindings before the document is ready
ko.applyBindings(vm, $("#computerInfo")[0]);
});
// create some random sample data
setInterval(function () {
var GB = 1024 * 1024;
['A', 'B', 'C', 'D'].forEach(function (name) {
hub.client.cpuInfoMessage({
machineName: name,
cpu: getRandomInt(0, 1000) / 10,
memUsage: getRandomInt(5 * GB, 7 * GB),
memTotal: 10 * GB
});
});
}, 1000);
});
// -------------------------------------------------------------------------------
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
.table { border-collapse: collapse; }
.table th, .table td { border: 1px solid silver; padding: 0 2px; }
.sortable { cursor: pointer; }
.table-striped tbody tr:nth-child(odd) { background-color: #f7f7f7; }
.highCpu { color: red; }
.lowMem { color: red; }
.right { text-align: right; }
.sorted { background-color: #B5E0FF; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div id="computerInfo">
<h2>Real-time System Resource Monitor</h2>
<h5 data-bind="visible: connected">Connected to message hub</h5>
<table border="0" class="table table-striped">
<thead>
<tr data-bind="foreach: headers">
<th class="sortable" data-bind="click: $root.toggleSort, text: title, css: {sorted: $data === $root.sortHeader()}"></th>
</tr>
</thead>
<tbody data-bind="foreach: machines">
<tr data-bind="foreach: $root.headers, css: conditions">
<td data-bind="text: $parent[key].formatted, css: cssClass"></td>
</tr>
</tbody>
</table>
</div>