2

我需要从敲除模板访问整个可观察数据上下文,而不仅仅是它的值。

在我正在开发的应用程序中,我经常使用大量元数据来帮助渲染视图。过去,我使视图模型属性变得复杂 - 将元数据和数据都存储为子属性(值属性中的值):

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>

但这是不优雅且不理想的。在有很多自动生成的情况下,这不仅不方便,而且实际上是一个主要障碍。我考虑过制作自定义绑定来包装模板绑定,但我希望有更好的解决方案。

这是我一直在为级联下拉菜单工作的一个真实示例。这个 JSFiddle有效,但那个 JSFiddle没有。

提前致谢。

4

1 回答 1

1

问题是淘汰赛在解包后将始终使用该值。如果它恰好是可观察的,您将失去这些子属性。您必须将您的 observable 重新包装到另一个对象中,这样您就不会像已经找到的那样丢失它。

您可以将其包装起来的一个好方法是为 subscribables(或任何更派生的类型)创建一个函数,该函数将执行此重新包装。您可以将所有单独的元数据附加到这个重新包装的对象上,也可以将它们打包到自己单独的对象中。您的代码可以再次优雅。

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 -->

更新的小提琴

于 2012-11-07T04:27:09.177 回答