Knockout计算不会更新视图中的属性

时间:2018-03-26 11:53:47

标签: javascript jquery knockout.js

我使用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>

1 个答案:

答案 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 *的牺牲品。请务必声明您的变量(尽可能在最窄的范围内)。

* (披露:这是我贫穷的小博客上的帖子)