【问题标题】:How do I access descendants from a different branch in the same viewModel?如何从同一 viewModel 中的不同分支访问后代?
【发布时间】:2012-09-30 07:36:25
【问题描述】:

我目前正在开发一个网站 spa 应用程序并尝试使用 knockout.js。它在一个 mvc 平台上,一个 dbdatacontroller api 使用数据和 javascript 视图模型的结果。我有一个复杂的视图模型并且遇到了困难,主要是因为是新手。我最大的问题似乎是访问 observables。数据库是这样组织的:

    function AdvanceSearch(data) {
var self = this;
self.AdvanceSearchID = ko.observable(data.AdvanceSearchID);
self.FieldTypeEnum = ko.observable(data.FieldTypeEnum);
self.AnswerType = ko.observable(data.AnswerType);
self.UserValues = ko.observableArray(ko.utils.arrayMap(data.UserValues, function (item) {
    return new UserValue(item);
}));
upshot.addEntityProperties(self, "AdvanceSearch:#A.Lib.Repository");
};

function UserValue(data) {
var self = this;

self.UserProfileID = ko.observable(data.UserProfileID);
self.LoginID = ko.observable(data.LoginID);
self.AdvanceSearchID = ko.observable(data.AdvanceSearchID);
self.FieldValueCount = ko.observable(data.FieldValueCount);
self.FieldValueText = ko.observable(data.FieldValueText);
    upshot.addEntityProperties(self, "UserValue:#A.Lib.Repository");
});
};


function AnswerType(data) {
var self = this;
self.AnswerTypeID = ko.observable(data.AnswerTypeID);
self.AnswerTypeText = ko.observable(data.AnswerTypeText);
self.Answers = ko.observableArray(ko.utils.arrayMap(data.Answers, function (item) {
    return new Answer(item);
}));
self.AnswerSliders = ko.observableArray(ko.utils.arrayMap(data.AnswerSliders, function (item) {
    return new AnswerSlider(item);
}));
upshot.addEntityProperties(self, "AnswerType:#A.Lib.Repository");
}

function Answer(data) {
var self = this;
self.AnswerTypeID = ko.observable(data.AnswerTypeID);
self.AnswerEnum = ko.observable(data.AnswerEnum);
self.AnswerText = ko.observable(data.AnswerText);
upshot.addEntityProperties(self, "Answer:#A.Lib.Repository");
}

function AnswerSlider(data) {
var self = this;
self.SliderID = ko.observable(data.SliderID);
self.AnswerTypeID = ko.observable(data.AnswerTypeID);
self.SliderType = ko.observable(data.SliderType);
self.Seed = ko.observable(data.Seed);
upshot.addEntityProperties(self, "AnswerSlider:#A.Lib.Repository");
}

而我的视图模型就是这样的:

function ASViewModel() {
// Private
var self = this;
var dataSourceOptions = {
    providerParameters: {
        url: "/api/Dating",
        operationName: "GetDatingProfile"
    },
    entityType: "AdvanceSearch:#A.Lib.Repository",
    bufferChanges: false,
    mapping: AdvanceSearch
};

// Public Properties
self.dataSource = new upshot.RemoteDataSource(dataSourceOptions)
                            .refresh();
self.AdvanceSearchs = self.dataSource.getEntities();
}

所以我的标记类似于

    <ol data-bind="foreach: AdvanceSearch">
   <!-- ko if: FieldTypeEnum()===5 -->
       <select data-bind="options: AnswerType().Answers, optionsText: 'AnswerText', optionsValue: 'AnswerEnum', optionsCaption: 'Not Specified', value: UserValues().FieldValueText"></select>
   <!-- /ko -->
    <!-- ko if: FieldTypeEnum()===11 -->
        <input type="text" class="multilinetext" data-bind="attr: { id: 'value_'+AdvanceSearchID()}, value: UserValues().FieldValueText" />
    <!-- /ko -->

所以基本上,无论我做什么,我似乎都无法访问项目的值。我访问过一个分支的任何地方,即。 AdvanceSearch().AnswerType().Answers,尝试获取 $parents[1].UserValues[].FieldValueText 似乎总是未定义。就像我说的,我是淘汰赛的新手,所以我可能只是错过了一些东西。还是我应该使用多个视图模型或类似的东西? (如果是这样,我该怎么做?) 谢谢。

【问题讨论】:

    标签: javascript asp.net-mvc-3 knockout.js upshot


    【解决方案1】:

    根据您的 cmets,我想我现在知道您要做什么。我将把它抽象出来,以便更容易理解,因为你的 ViewModel 和 Objects 非常复杂,不解释就不容易理解。

    如果你有对象类型,BookAuthor 带有构造函数:

    function Book(data) {
        this.ISBN = ko.observable( data.ISBN );
        this.Title = ko.observable( data.Title );
        this.AuthorID = ko.observable( data.AuthorID );
        /* other properties */
    }
    
    function Author(data) {
        this.AuthorID = ko.observable( data.AuthorID );
        this.Name = ko.observable( data.Name );
        /* other properties */
    }
    

    要从select 框中选择一本书并将input 显示绑定到作者姓名,您将需要视图模型:

    var vm = {};
    vm.books = ko.observableArray( /* initial data */ );
    vm.authors = ko.observableArray( /* initial data */ );
    vm.selectedBook = ko.observable(null);
    vm.selectedBooksAuthor = ko.computed( function() {
        var authors = vm.authors(),
            selectedBook = vm.selectedBook();
        if( !selectedBook ) return null; // cannot get .AuthorID() if selectedBook == null
        var id = selectedBook.AuthorID();
        for( var a in authors ) {
            if( authors[a].AuthorID() == id )
                return authors[a];
        }
    })
    

    还有你的 HTML:

    <select data-bind="options: books,
                       optionsText: 'Title',
                       optionsCaption: 'Select a book...',
                       value: selectedBook"></select>
    <!-- ko with: selectedBooksAuthor -->
    <input type="text" data-bind="value: Name" />
    <!-- /ko -->
    

    请注意,这对于大量数据集可能会变得非常低效 - 考虑为您的 computed observableselectedBooksAuthor 使用更好的搜索算法(而不是此处使用的“线性”)

    编辑:

    请参阅this fiddle 进行工作演示。

    【讨论】:

    • 啊。我想我现在明白了。因此,对于选定的值,将它们保留为 null,然后通过计算的 observable 将它们链接到另一个数组的值。这是有道理的。
    • 好的,所以我不确定一件事:id = vm.selectedBook().AuthorID(); 来自哪里?我看到 selectedBook() 是一个 observable,但它是否受 select 约束?
    • 是的。通过在 select 的 data-bind 属性中设置 value: selectedBook,您是在告诉 knockout 用当前选择填充 observable selectedBook
    • 所以,我一直在努力让这个例子在fiddle 工作。它真的不喜欢空值,而且我认为它不知道selectedBook().AuthorID() 是什么。是否应该在某处明确定义?
    • rewrote the fiddle 让它更干净一些,然后找出问题所在并修复your fiddle
    【解决方案2】:

    你的:

    <ol data-bind="foreach: AdvanceSearch">
    

    需要:

    <ol data-bind="foreach: AdvanceSearchs">
    

    注意 AdvanceSearch 与 AdvanceSearchs

    的复数形式

    编辑:

    另外,UserValues 是一个 Observable 数组,因此您无法访问 UserValues().FieldValueText

    请注意确定您的预期输出,但这会显示每个 UserValue 的输入:

    <!-- ko foreach: UserValues -->
        <input type="text" class="multilinetext" data-bind="attr: { id: 'value_'+UserProfileID()}, value: FieldValueText" />
    <!-- /ko -->
    

    注意:我已将输入的 ID 更改为其他内容以避免重复 ID - 这假设 UserProfileID 对于 UserValue 来说是唯一的

    进一步,

    我可以向您推荐Knockouts options-binding 的文档。 select 元素的数据绑定选项是:

    options: - 这是将select 元素绑定到的选项列表

    optionsText: 对于列表中的每一项,要在select 元素中显示的属性

    optionsCaption:select 元素的第一个/“虚拟”项的文本

    value:这应该是一个可观察的并且绑定到select元素的当前选择

    value 绑定是 Knockout 的一个非常方便的功能。由于您可以访问整个选定的对象,因此无需担心 ID - 以及 optionsValue

    所以你的select 绑定看起来像:

    <select data-bind="options: AnswerType().Answers,
                       optionsText: 'AnswerText',
                    @* optionsValue: 'AnswerEnum', *@
                       optionsCaption: 'Not Specified',
                       value: selectedAnswer"></select>
    

    假设您已将一个可观察的selectedAnswer 添加到您的视图模型中。

    【讨论】:

    • 所以要访问可观察数组中的可观察对象,我总是需要一个 foreach?有什么方法可以在 select 中切换上下文,以便我的 value 选项可以指向另一个 observable 数组的 observable?
    • 选择您的选择,例如value:UserValues()[someindextakenfromadvancesearchs].selectedAnswer?并感谢您发现该错字(AdvanceSearchs)。
    • 我认为您对 value: 绑定感到困惑。 selectedAnswer 此处指的是在您的 select 框中选择的项目 - 它不是您的 UserValue 对象的属性。我想我现在知道您要做什么,请参阅我的其他答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    相关资源
    最近更新 更多