Data binding 如何访问数据上下文';从knockoutjs模板内部可以观察到

Data binding 如何访问数据上下文';从knockoutjs模板内部可以观察到,data-binding,knockout.js,Data Binding,Knockout.js,我需要从淘汰模板访问整个可观察数据上下文,而不仅仅是它的值 在我正在开发的应用程序中,我经常使用大量元数据来帮助一般地呈现视图。在过去,我使视图模型属性变得复杂-将元数据和数据存储为子属性(值属性中包含值): 我正在改变这些元数据,使之成为可观测的属性(正如我相信Ryan Niemeyer在他的一篇博客文章或会议中所描述的)。我发现它更干净、更优雅,而且通常更易于维护,开销更少(特别是在序列化方面)。与上述示例等效的内容如下: ViewModel.AwesomeProperty = ko.obs

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

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

我正在改变这些元数据,使之成为可观测的属性(正如我相信Ryan Niemeyer在他的一篇博客文章或会议中所描述的)。我发现它更干净、更优雅,而且通常更易于维护,开销更少(特别是在序列化方面)。与上述示例等效的内容如下:

ViewModel.AwesomeProperty = ko.observable('Awesome Value');
ViewModel.AwesomeProperty.label = 'My Awesome Label';
ViewModel.AwesomeProperty.template = 'awesomeTemplate';
ViewModel.AwesomeProperty.otherMetaData = 'etc';
这样做的副作用是,将ViewModel.AwesomeProperty传递给模板会将数据上下文设置为可观察值(在本例中为“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>


问题是,knockout在展开后总是使用该值。如果它恰好是一个可观察的,那么您将丢失这些子属性。你必须将你的可观察物体重新包装成另一个物体,这样你就不会像你已经找到的那样丢失它

一个很好的方法是为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 -->

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