我使用knockout的计算来显示过滤的数据集。它是通过更改可观察的过滤器触发的。 我想显示一个列表,其中包含匹配的条目和一个不匹配条目的列表以及它不匹配的原因。 所以基本上我在条目中添加了一个新属性,其中包括排除条目的原因。第一次渲染时工作正常,新属性显示正确的排除原因,每当我更改过滤器时,列表都被正确更新但排除原因在视图中保持不变,即使它们在可观察返回的结果中不同。
为了使错误可视化,我做了最小的工作示例。 在示例中,条目“joe”被排除,并且排除原因是正确的,如果您将filter2更改为“small”,则应该有一个额外的原因,即如果单击“show actual”按钮,joe将被排除但不显示它只是渲染计算的实际输出,你会看到正确的排除原因。
那么为什么他们不会在视图中自动更改?有什么方法可以达到我期望的结果吗?
JSFIDDLE EXAMPLE TO PLAY AROUND
这里是js代码(jquery和knockout)
var viewModel = function() {
var self = this;
self.filter1 = ko.observable("green");
self.filter2 = ko.observable("big");
self.entries = ko.observableArray([
{name:'Joe',f1:'yellow',f2:'big'},
{name:'Brad',f1:'red',f2:'small'},
{name:'Anton',f1:'green',f2:'big'},
{name:'Pete',f1:'red',f2:'small'},
{name:'Cedric',f1:'green',f2:'smaller'}
]);
self.filteredEntries = ko.computed( function() {
filter1 = self.filter1();
filter2 = self.filter2();
ens_excluded = [];
ens = jQuery.grep(self.entries(), function( n, i ) {
expression = true;
excludeReasons =[];
t1 = n.f1 == filter1
if(!t1){
expression = false;
excludeReasons.push("f1 no match")
}
t2 = n.f2 == filter2;
if(!t2){
expression = false;
excludeReasons.push("f2 no match")
}
if(!expression){
n.excludeReasons = excludeReasons;
ens_excluded.push(n)
}
return expression;
});
return {
include:ens,
exclude:ens_excluded
};
});
};
vm = new viewModel();
ko.applyBindings(vm);
$(document).on("click",".showActual",function(){
ens_excluded = vm.filteredEntries().exclude;
$('.actual').empty();
$.each(ens_excluded, function(i,val){
$('.actual').append("<p><span>"+val.name+"("+val.f1+","+val.f2+")</span> <small>"+val.excludeReasons.toString()+"</small></p>")
})
})
<div class="body">
<div>
<label for="fname">Filter1:</label>
<select name="" data-bind="value: vm.filter1" id="">
<option value="green">green</option>
<option value="red">red</option>
</select>
<label for="fname">Filter2:</label>
<select name="" data-bind="value: vm.filter2" id="">
<option value="big">big</option>
<option value="small">small</option>
</select>
</div>
<hr />
<strong>included:</strong>
<div data-bind="foreach:vm.filteredEntries().include">
<p data-bind="text:$data.name+' ('+$data.f1+', '+$data.f2+')'"></p>
</div>
<hr>
<strong>excluded:</strong>
<div data-bind="foreach:vm.filteredEntries().exclude">
<p> <span data-bind="text:$data.name+' ('+$data.f1+', '+$data.f2+')'"></span> <small data-bind="text:$data.excludeReasons.toString()"></small> </p>
</div>
<button class="showActual" >Show Acutal</button>
<div class="actual">
</div>
</div>
<script src="https://ajax.googleapis.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>
答案 0 :(得分:1)
问题是foreach
的主题不是计算的(vm.filteredEntries
),它是计算的对象作为属性(vm.filteredEntries().exclude
)的数组:
<div data-bind="foreach:vm.filteredEntries().exclude">
<p> <span data-bind="text:$data.name+' ('+$data.f1+', '+$data.f2+')'"></span> <small data-bind="text:$data.excludeReasons.toString()"></small> </p>
</div>
相反,请确保您以某种方式直接依赖计算机。有几种方法,但不更改您的viewmodel,with
有效:
<!-- ko with: vm.filteredEntries -->
<div data-bind="foreach:exclude">
<p> <span data-bind="text:$data.name+' ('+$data.f1+', '+$data.f2+')'"></span> <small data-bind="text:$data.excludeReasons.toString()"></small> </p>
</div>
<!-- /ko -->
示例:
var viewModel = function() {
var self = this;
self.filter1 = ko.observable("green");
self.filter2 = ko.observable("big");
self.entries = ko.observableArray([
{name:'Joe',f1:'yellow',f2:'big'},
{name:'Brad',f1:'red',f2:'small'},
{name:'Anton',f1:'green',f2:'big'},
{name:'Pete',f1:'red',f2:'small'},
{name:'Cedric',f1:'green',f2:'smaller'}
]);
self.filteredEntries = ko.computed( function() {
filter1 = self.filter1();
filter2 = self.filter2();
ens_excluded = [];
ens = jQuery.grep(self.entries(), function( n, i ) {
expression = true;
excludeReasons =[];
t1 = n.f1 == filter1
if(!t1){
expression = false;
excludeReasons.push("f1 no match")
}
t2 = n.f2 == filter2;
if(!t2){
expression = false;
excludeReasons.push("f2 no match")
}
if(!expression){
n.excludeReasons = excludeReasons;
ens_excluded.push(n)
}
return expression;
});
return {
include:ens,
exclude:ens_excluded
};
});
};
vm = new viewModel();
ko.applyBindings(vm);
$(document).on("click",".showActual",function(){
ens_excluded = vm.filteredEntries().exclude;
$('.actual').empty();
$.each(ens_excluded, function(i,val){
$('.actual').append("<p><span>"+val.name+"("+val.f1+","+val.f2+")</span> <small>"+val.excludeReasons.toString()+"</small></p>")
})
})
<div class="body">
<div>
<label for="fname">Filter1:</label>
<select name="" data-bind="value: vm.filter1" id="">
<option value="green">green</option>
<option value="red">red</option>
</select>
<label for="fname">Filter2:</label>
<select name="" data-bind="value: vm.filter2" id="">
<option value="big">big</option>
<option value="small">small</option>
</select>
</div>
<hr />
<strong>included:</strong>
<!-- ko with: vm.filteredEntries() -->
<div data-bind="foreach:include">
<p data-bind="text:$data.name+' ('+$data.f1+', '+$data.f2+')'"></p>
</div>
<!-- /ko -->
<hr>
<strong>excluded:</strong>
<!-- ko with: vm.filteredEntries -->
<div data-bind="foreach:exclude">
<p> <span data-bind="text:$data.name+' ('+$data.f1+', '+$data.f2+')'"></span> <small data-bind="text:$data.excludeReasons.toString()"></small> </p>
</div>
<!-- /ko -->
<button class="showActual" >Show Acutal</button>
<div class="actual">
</div>
</div>
<script src="https://ajax.googleapis.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>
附注:通过不声明grep
回调中使用的几个变量,代码也成为The Horror of Implicit Globals *的牺牲品。请务必声明您的变量(尽可能在最窄的范围内)。
* (披露:这是我贫穷的小博客上的帖子)