我需要从淘汰模板访问整个可观察数据上下文,而不仅仅是它的值。
在我正在开发的应用程序中,我经常使用大量元数据来帮助一般地渲染视图。在过去,我使视图模型属性变得复杂 - 将元数据和数据存储为子属性(值属性中的值):
ViewModel.AwesomeProperty = {
value: ko.observable('Awesome Value'),
label: 'My Awesome Label',
template: 'awesomeTemplate',
otherMetaData: 'etc'
}
我正在更改此元数据以成为可观察对象的属性(我相信Ryan Niemeyer在他的一篇博文或会话中描述)。我发现它更干净,更优雅,并且通常更易于维护且开销更少(特别是在序列化方面)。与上述示例等效的内容如下:
ViewModel.AwesomeProperty = ko.observable('Awesome Value');
ViewModel.AwesomeProperty.label = 'My Awesome Label';
ViewModel.AwesomeProperty.template = 'awesomeTemplate';
ViewModel.AwesomeProperty.otherMetaData = 'etc';
这样做的副作用是将ViewModel.AwesomeProperty传递给模板会将数据上下文设置为observable的值(在本例中为'Awesome Value'),使元数据无法从$ data中访问:
<script id="example" type="text/html">
<!-- This won't work anymore -->
<span data-bind="text: $data.label></span>
</script>
<div data-bind="template: {name: 'example', data: AwesomeProperty}"></div>
我现在的解决方法是将数据值包装在匿名对象中,如下所示:
<script id="example" type="text/html">
<!-- Now it works again -->
<span data-bind="text: data.label></span>
</script>
<div data-bind="template: {name: 'example', data: {data:AwesomeProperty}}"></div>
但这不够优雅且不理想。在存在大量自动生成的情况下,这不仅不方便,而且实际上是主要的障碍。我已经考虑过自定义绑定来包装模板绑定,但我希望有更好的解决方案。
这是一个真实世界的例子,我一直在为级联下拉工作。 This JSFiddle有效,但that JSFiddle没有。
提前致谢。
答案 0 :(得分:1)
事实上,淘汰赛将在展开后始终使用该值。如果它恰好是一个可观察的,你将失去那些子属性。您必须将您的observable重新包装到另一个对象中,这样您就不会丢失它,就像您已经找到的那样。
一个很好的方法可以解决这个问题,就是为可编辑的(或任何更多派生类型)创建一个函数来完成这个重写。您可以将所有单独的元数据添加到此重新包装的对象上,也可以将它们打包到各自的单独对象中。你的代码可以再次优雅。
var buildSelection = function (choices, Parent) {
return _(ko.observable()).extend({
// add the metadata to a 'meta' object
meta: {
choices: choices,
availableChoices: ko.computed(function () {
if (!Parent) return choices;
if (!Parent()) return [];
return _(choices).where({ ParentID: Parent().ID });
})
}
});
}
ko.subscribable.fn.templateData = function (metaName) {
return {
// access the value through 'value'
value: this,
// access the metadata through 'meta'
meta: this[metaName || 'meta'] // meta property may be overridden
};
}
然后在绑定中,调用此函数以创建重新包装的对象。只需记住在模板中调整绑定即可。
<script id="Selection" type="text/html">
<select data-bind="
options: meta.availableChoices,
optionsText: 'Value',
value: value,
optionsCaption: 'Select One',
enable: meta.availableChoices().length
"></select>
</script>
<!-- ko template: { 'name': 'Selection', 'data': Level1.templateData() } --><!-- /ko -->
<!-- ko template: { 'name': 'Selection', 'data': Level2.templateData() } --><!-- /ko -->
<!-- ko template: { 'name': 'Selection', 'data': Level3.templateData() } --><!-- /ko -->