我是Knockout的新手,所以请耐心等待。我有一个数据表,我试图从表格标题部分的文本框中动态过滤。我只能在文本框中添加一个字符,然后过滤数据并且控件失去焦点。例如,我无法输入" XX"因为过滤动作是在第一个" X"输入并从文本框中删除光标。
这是我的灵感 - http://jsfiddle.net/Xpx7f/20/
这是标记
// This where I'm filtering
<thead>
<tr data-bind="foreach: columnNames">
<td>
// why isn't this "data-bind="textInput: filters"?
// the observable name is filters not filter
<input type='text' data-bind="textInput: filter" />
</td>
</tr>
</thead>
<tbody data-bind="foreach: enrollments">
<tr>
<td data-bind="text: WinId"></td>
<td data-bind="text: EffectiveYear"></td>
<td data-bind="text: FileKeeperGroupId"></td>
</tr>
</tbody>
这里是淘汰赛
var viewModel = (function(){
self = this;
self.enrollments = ko.observableArray([]);
self.filters = ko.observableArray([]);
self.filteredItems = ko.computed(function () {
var filter = self.filters();
var enrollments = ko.utils.arrayFilter(self.enrollments(), function (item) {
for (var col in filter) {
var v = (item[col] || '').toString(); // column value
var f = filter[col]; // what's typed in header
if (v.lastIndexOf(f, 0) === 0) return true;
}
return false;
});
if(enrollments.length > 0)
{
self.enrollments(enrollments);
}
else{
return self.enrollments();
}
});
var subscriptions = [];
self.columnNames = ko.computed(function () {
ko.utils.arrayForEach(subscriptions, function (s) { s.dispose(); });
subscriptions = [];
if (self.enrollments().length === 0) return [];
var props = [];
var obj = self.enrollments()[0];
for (var name in obj) {
var p = { name: name, filter: ko.observable('') };
subscriptions.push(p.filter.subscribe(filterOnChanged, p));
props.push(p);
}
return props;
});
var filterOnChanged = function (value) {
var filters = self.filters();
filters[this.name] = value;
self.filters(filters);
};
});
$(document).ready(function(){
var vm = new viewModel();
vm.getRecords();
ko.applyBindings(viewModel);
});
答案 0 :(得分:0)
看起来这种情况正在发生,因为您的 self.columnNames 是一个计算的可观察对象,它取决于 self.enrollments observable。应用过滤器时, self.enrollments observable会发生变化。当你的 self.columnNames observable得到更新时,重新绑定就会发生,一切都会被重新渲染,你就会失去焦点,因为DOM元素是新的。
我会移动逻辑以在单独的函数中构建列:
function initColumns () {
ko.utils.arrayForEach(subscriptions, function (s) { s.dispose(); });
subscriptions = [];
if (self.enrollments().length === 0) return [];
var props = [];
var obj = self.enrollments()[0];
for (var name in obj) {
var p = { name: name, filter: ko.observable('') };
subscriptions.push(p.filter.subscribe(filterOnChanged, p));
props.push(p);
}
return props;
});
然后在初始化columnNames可观察数组时调用此函数。
self.columnNames = ko.observableArray(initColumns());
否则你会陷入这种自我更新的循环地狱。
基于小提琴更新
好的,你要做的就是删除self.filteredItems并创建一个如下所示的计算observable:
self.filteredEnrollments = ko.computed(function () {
var filter = self.filters();
return ko.utils.arrayFilter(self.enrollments(), function (item) {
for (var col in filter) {
var v = (item[col] || '').toString(); // column value
var f = filter[col]; // what's typed in header
if (v.lastIndexOf(f, 0) === 0) return true;
}
return false;
});
});
在你的html中,绑定到filteredEnrollments而不是注册。
<tbody data-bind="foreach: filteredEnrollments">
<tr>
<td data-bind="text: WinId"></td>
<td data-bind="text: EffectiveYear"></td>
<td data-bind="text: FileKeeperGroupId"></td>
</tr>
</tbody>
这样您就不会更改注册可观察数组,该数组不会触发计算重新绑定DOM的 columnNames 。