我有一个包含由foreach
绑定生成的行的表。
与'通常'KO表的唯一区别在于,对于行的动态过滤,我在visible
元素上使用tr
绑定,该绑定绑定到使用某些{的值的函数{1}}来判断当前行是否应该可见:
observable
我正在使用这种方法,因为它在性能方面比使用<table>
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: $root.rowFilter($data)">
...
</tr>
</tbody>
</table>
绑定的集合的传统操作要好得多,并且导致不断插入\删除DOM节点。
这里唯一的问题是没有用于交替行颜色的纯CSS解决方案。 foreach
节点保留在DOM中,tr
选择器赢了“如果某些:nth-child()
不可见,则无法正常工作。
所以,我不得不坚持使用jQuery解决方案:
tr
但当function stripeVisibleRows(tableElem) {
var $visibleRows = $(tableElem).find('tr:visible');
$visibleRows.filter(':odd').css('background-color', '#EEEAE7');
$visibleRows.filter(':even').css('background-color', '#DED7D1');
};
使用的任何可观察对象触发更新visible
绑定时,如何在执行所有行的rowFilter
绑定后,如何在Knockout中调用此函数?吗
我已尝试visible
假subscribe
取决于computed
功能:
rowFilter
,编写像这样的自定义绑定:
self.rowFiltering = ko.computed(function () {
return self.rowFilter();
})
.subscribe(function () {
tableHelpers.stripeVisibleRows('.tbl');
});
及其用法:
// Stripes visible tr elements.
// Must be applied to a table element.
// Must be bound to the same expression as used to change visibility of the tr elements.
ko.bindingHandlers.stripeVisibleRows = {
update: function (element, valueAccessor) {
// Read provided binding value to establish dependencies tracking
ko.utils.unwrapObservable(valueAccessor());
tableHelpers.stripeVisibleRows(element);
},
after: ['visible']
};
但是使用这两种方法我的jQuery条带化函数在应用<table data-bind="stripeVisibleRows: $root.rowFilter()">
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: $root.rowFilter($data)">
...
</tr>
</tbody>
</table>
绑定之前被调用,因此无法正常工作。
有人可以建议如何实现我想要做的事情吗?
也许我甚至需要改变过滤和条带化行的整个方法,但它应该没有插入\删除DOM节点,并尽可能干净和可重复使用。
答案 0 :(得分:2)
您可以订阅rowFilter
的更改,然后将通话的执行推迟到stripeVisibleRows
,以确保淘汰赛使用setTimeout(...,0)
pattern更新所有内容:
self.rowFilter.subscribe(function() {
setTimeout(function() {
tableHelpers.stripeVisibleRows('.tbl');
}, 0);
});
如果您不介意从DOM中删除/添加元素,则可以使用if
binding代替visible
- 这会从DOM中完全删除标记,而不是只是隐藏它:
<table>
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="if: $root.rowFilter($data)">
...
</tr>
</tbody>
</table>
答案 1 :(得分:2)
你必须在幕后做所有事情,并通过功能公开它。条带化可以使用CSS类完成,因此您不必担心使用jQuery。
由于数据项不会发生变化,因此它们只是一个不可观察的数组。在幕后,我们有一个基于rowFilter
的计算器,它访问一些可观察的。 foreach调用函数来查看计算中与当前项对应的项,以设置可见性和类。
在此示例中,您将按字母数过滤元素。
var tracker;
vm = {
letters: ko.observable(3),
unfilteredItems: ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'],
visible: function(idx) {
return tracker()[idx].visible;
},
odd: function(idx) {
return tracker()[idx].odd;
}
};
function rowFilter(item) {
return item.length === vm.letters();
}
tracker = ko.pureComputed(function() {
var visibleCount = 0;
var result = ko.utils.arrayMap(vm.unfilteredItems, function(item) {
var visible = rowFilter(item);
if (visible) ++visibleCount;
return {
visible: visible,
odd: visibleCount % 2
};
});
return result;
});
ko.applyBindings(vm);
&#13;
tr {
background-color: lightgreen;
}
tr.odd {
background-color: yellow;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="value:letters, options:[3,4,5]"></select>
<table border="1">
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: $root.visible($index()) , css: {odd:$root.odd($index())} ">
<td data-bind="text:$data"></td>
</tr>
</tbody>
</table>
&#13;
答案 2 :(得分:1)
而不是在表格渲染后计算所有行条纹(即:使用stripeVisibleRows
),而是将当前循环$index
传递给单独的$root
函数,并让它确定当前条带对于该索引的作用。例如:
HTML:
<style>
.On { background-color: #CCC }
.Off { background-color: #FFF }
</style>
<table id='test'>
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: IsVisible, css: $root.rowColour($index)">
<td data-bind='text: Text'></td>
</tr>
</tbody>
</table>
使用Javascript:
var vmItem = function(visible, text) {
this.IsVisible = visible;
this.Text = text;
}
var vm = {
unfilteredItems: [
new vmItem(true, "Some"),
new vmItem(true, "Data"),
new vmItem(false, "Not Shown"),
new vmItem(true, "Shown"),
new vmItem(true, "To"),
new vmItem(false, "Blah!"),
new vmItem(true, "The User")
],
rowColour: function(index) {
var toggle = true;
for(var c = 0; c < index(); c++) {
// if the row at this index is visible, flip the row toggle
if (this.unfilteredItems[c].IsVisible) toggle = !toggle;
}
return toggle ? "On" : "Off";
}
};
ko.applyBindings(vm, $('#test')[0]);
答案 3 :(得分:1)
使用@ JamesThorpe关于setTimeout(...,0)
pattern的建议,我设法让我的问题中的自定义绑定正常工作:
// Stripes visible tr elements.
// Must be applied to a table element.
// Must be bound to the same expression as used to change visibility of the tr elements.
ko.bindingHandlers.stripeVisibleRows = {
update: function (element, valueAccessor) {
// Read provided binding value to establish dependencies tracking
ko.utils.unwrapObservable(valueAccessor());
setTimeout(function () {
tableHelpers.stripeVisibleRows(element);
}, 0);
}
};
function stripeVisibleRows(tableElem) {
var $visibleRows = $(tableElem).find('tr:visible');
$visibleRows.filter(':odd').css('background-color', '#EEEAE7');
$visibleRows.filter(':even').css('background-color', '#DED7D1');
};
我像这样使用这个绑定:
<table data-bind="stripeVisibleRows: $root.rowFilter()">
<thead>
...
</thead>
<tbody data-bind="foreach: unfilteredItems">
<tr data-bind="visible: $root.rowFilter($data)">
...
</tr>
</tbody>
</table>
这是性能和可重用性方面的最佳解决方案我到目前为止已动态过滤表格及其行的替代颜色。
记住两件事:
stripeVisibleRows
绑定绑定到同一表达式
当您用于tr
s visibility
绑定以保持过滤和
交替同步。他们之间唯一的区别就是你
不要通过$data
绑定中的stripeVisibleRows
表达stripeVisibleRows
绑定到table
元素。答案 4 :(得分:1)
要实现完整的淘汰赛解决方案,您需要在行本身中公开可见性和条带化,并在视图模型中计算它们。
请记住,javascript是动态的,因此您可以轻松地向行添加新属性。
由于您没有显示您的视图模型,我将解释您如何做到这一点,并包含一个示例视图模型来执行此操作:
row.visible = ko.computed(function() { /*your code here*/});
row.visible = ko.observable(/*your initial value*/);
visible
可观察的订阅新函数,并使用它来更新每行中的条带 注意:为了避免在计算中使用循环,可以使用像lodash
这样的库来简化数组/集合操作。
您可以在this fiddle;
中看到此解决方案HTML:
<ul data-bind="foreach: rows">
<li data-bind="text: val(), visible: visible, attr:{class: striping}">
</li>
</ul>
Show only rows that contain:
<input type="text" data-bind="value: mustContain"/>
Javascript(它使用lodash。请参见底部的注释):
var Vm = function(_rows) {
var self = this;
// filter condition on view model
self.mustContain = ko.observable('');
// rows array
self.rows = _rows;
// this will update striping
var updateStriping = function() {
var visibleRows = _.filter(rows,function(r) {
return r.visible();
});
_.forEach(visibleRows, function(r,i) {
r.striping(i % 2 ? 'odd' : 'even');
});
};
_.forEach(self.rows, function(row) {
// make observable version of value
row.val = ko.observable(row.value);
// add visibility to each row
row.visible = ko.computed(function() {
return row.val().match(self.mustContain());
});
// add striping to each row
row.striping = ko.observable('');
// subscribe visible change
row.visible.subscribe(updateStriping);
});
updateStriping();
return self;
};
var rows = [
{ value: 'alpha' },
{ value: 'beta' },
{ value: 'gamma' },
{ value: 'delta' },
{ value: 'epsilon' },
{ value: 'zeta' },
{ value: 'eta' },
{ value: 'theta' },
{ value: 'iota' },
{ value: 'kappa' },
{ value: 'lambda' },
{ value: 'mu' },
{ value: 'nu' },
{ value: 'xi' },
{ value: 'omicron' },
{ value: 'pi' },
{ value: 'ro' },
{ value: 'sigma' },
{ value: 'tau' },
{ value: 'upsilon' },
{ value: 'phi' },
{ value: 'chi' },
{ value: 'psi' },
{ value: 'omega' }];
var vm = new Vm(rows);
ko.applyBindings(vm);
和CSS:
.odd { background-color: silver; }
.even {}
注意:此代码使用attr
绑定来设置条带化类。您通常会使用css
代替。
关于lodash和EcmaScript 5的注释(感谢Alexander Abakumov的评论):most modern browsers support ES5,它提供了一些与lodash相同的功能。在这种情况下,您可以删除lodash,并使用原生filter和forEach以这种方式更改代码:
var visibleRows = _.filter(rows,function(r) { // Line 9
var visibleRows = rows.filter(function(r) {
_.forEach(visibleRows, function(r,i) { // Line 12
visibleRows.forEach(function(r,i) {
_.forEach(self.rows, function(row) { // Line 16
self.rows.forEach(function(row) {