2012-12-18 59 views
3

我有一个可观察的服务数组,可能由某个公司(即Sales,Service,Consulting)提供。这些服务中的每一项都可由公司在美国零个或多个州执行,并且每个州都有一个可选的认证号码。因此,该服务具有可观察的位置阵列,位置是状态和证书编号的组合。Knockout:与可观察数组和复选框的复杂绑定

function Service(name) { 
    var self = this; 

    self.Name = ko.observable(name); 
    self.Locations = ko.observableArray(); 
} 

function Location(state) { 
    var self = this; 

    self.State = ko.observable(state); 
    self.CertNum = ko.observable(); 
} 

在用户界面中,我想显示美国所有州的单一列表。用户会勾选公司运营所在州的每个州旁边的复选框,从而制作州的“主列表”。这个主列表反过来驱动出现在每个可能的服务下的选择。

例如,用户在主列表中勾选“阿拉巴马州”和“亚利桑那州”。 UI然后为每个服务呈现单独的位置作为复选框和文本框的组合。这里的主视图模型:

function viewModel() { 
    var self = this; 

    self.AllStates = [ 
     { name: "Alabama", abbrev: "AL" }, 
     { name: "Alaska", abbrev: "AK" }, 
     { name: "Arizona", abbrev: "AZ" }, 
     { name: "Arkansas", abbrev: "AR" } 
    ]; 

    self.BusinessStates = ko.observableArray(); 

    self.AllSelectedStates = ko.computed(function() { 
     return ko.utils.arrayFilter(self.AllStates, function(item) { 
      return self.BusinessStates.indexOf(item.abbrev) >= 0; 
     }); 
    }); 

    self.Services = ko.observableArray([ 
     new Service("Sales"), 
     new Service("Service"), 
     new Service("Consulting") 
    ]); 
} 

一个简单的观点看起来像:

<h2>Master List</h2> 
<ul data-bind="foreach: AllStates"> 
    <li> 
     <label><input type="checkbox" data-bind="value: abbrev, checked: $root.BusinessStates"/> <span data-bind="text: name"></span></label></li> 
</ul> 

<h2>Services</h2> 
<div class="service" data-bind="foreach: Services"> 
    <h3 data-bind="text: Name"></h3> 
    <ul data-bind="foreach: $root.AllSelectedStates"> 
     <li> 
      <label> 
      <input type="checkbox" data-bind="value: abbrev, checked: $parent.Locations" /> 
      <span data-bind="text: name"></span> 
      </label> 
      <input type="text" placeholder="Cert. Number" data-bind="value: ??" /> 
     </li> 
    </ul> 
</div> 

至少查看得到我的状态进入了服务的位置排列,但我不知道该怎么绑定CertNum属性。

假设用户在主列表中选择了阿拉巴马州和亚利桑那州。现在,对于“销售”服务,用户勾选“亚利桑那州”并输入认证号码“98765”。在“咨询”服务下,用户勾选“阿拉巴马州”并且不输入证书编号。理想情况下,我想生成的JSON片段看起来像:

"Services": [ 
    { 
     "Name": "Sales", 
     "Locations": [ 
      { 
       "State": { "name": "Arizona", "abbrev": "AZ" }, 
       "CertNum": "98765" 
      } 
     ] 
    }, 
    { 
     "Name": "Service", 
     "Locations": [] 
    }, 
    { 
     "Name": "Consulting", 
     "Locations": [ 
      { 
       "State": { "name": "Alabama", "abbrev": "AL" }, 
       "CertNum": null 
      } 
     ] 
    } 
] 

我可以渲染并结合美国各州的“主列表”。我可以迭代服务并呈现一个UI,为每个可用服务位置(即状态复选框,证书编号文本框)提供控件。我无法绑定服务位置控件以控制相应位置数组中位置的成员资格,并同时影响证书编号的值。

我所做的最接近的尝试是非常复杂和黑客,我使用knockout-postBox plugin将viewModel.AllSelectedStates属性链接到每个服务模型上的类似属性,然后将新位置推送到每个Service.Locations中阵列。它几乎的作品,除非它擦除了对服务的更改,只要主列表中选中或取消选中状态。此外,我必须将每个可用位置都推送到每个服务的位置数组中,并依靠该位置上的IsSelected布尔标志来确定它是否已被选中(我宁愿将只是数组中被选中的位置)。

你可以看到在此的jsfiddle尝试:http://jsfiddle.net/cbono/PU6Sq/23/

关于它的好处是,它表明了用户界面应该如何表现。但是我认为没有postBox插件这是可能的,我只是没有找到解决问题的正确方法。我已经花了好几天的时间来解决这个问题,并且不能提出一个100%的解决方案。

回答

0

使用visible绑定更容易,而不是管理收集事件。

<li data-bind="visible: State().IsSelected"> 

http://jsfiddle.net/MizardX/XTkpb/


如果你真的需要来处理它们,你可以在Services对象缓存StateAndCert对象:

var self = this, 
    cache = {}; 

self.Locations = ko.computed(function() { 
    return ko.utils.arrayMap(states(), function (item) { 
     var key = item.Abbrev(); 
     if (cache[key] === undefined) { 
      cache[key] = new StateAndCert(item); 
     } 
     return cache[key]; 
    }); 
}); 

http://jsfiddle.net/MizardX/HMAtE/


要隐藏从JSON渲染某些属性,你可以的东西他们function(){}属性里面:

self.Meta = function(){}; 
self.Meta.IsSelected = ko.observable(false); 
+0

谢谢,马库斯。我没有完全使用你的解决方案,但你帮助我走上了正确的道路。从一个模型向另一个模型发送可观察的项目数组是我没有想到的。但是,我并没有发送整个状态数组,而是创建了一个依赖的可观察值,该可观察值被过滤到所选的状态并使用它。像魅力一样工作! – CBono