简体   繁体   中英

How do I access descendants from a different branch in the same viewModel?

I am currently working on a website spa application and attempting to use knockout.js. it's on an mvc platform with and a dbdatacontroller api using upshot for data and javascript view models. I have a complex view model and am running into difficulty, mainly due to being new to knockout. My biggest problem seems to be in accessing the observables. The database is organized thusly:

    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");
}

And my view model is just this:

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();
}

So my markup is something like

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

So basically, no matter what I do, I cannot seem to access the values of the items. Anywhere I have accessed the one branch, ie. AdvanceSearch().AnswerType().Answers , trying to get $parents[1].UserValues[].FieldValueText seems always to be undefined. Like I said, I'm new to knockout, so I'm probably just missing something. Or should I be using multiple viewmodels or something similar? (And if so, how would I do that?) Thanks.

Your:

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

needs to be:

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

Note the pluralisation of AdvanceSearch to AdvanceSearchs

Edit:

Also, UserValues is an Observable Array, hence you cannot access UserValues().FieldValueText .

Note sure exactly what your expected output is, but this displays an input per UserValue :

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

NB: I've changed, the ID of the input to something else to avoid duplicate IDs -- this assumes UserProfileID is unique per UserValue

Further,

May I refer you to the documentation for Knockouts options-binding . Your data-bind options for a select element are:

options: - This is the list of options to bind the select element to

optionsText: For each item in the list, the property to display in the select element

optionsCaption: The text for the first/'dummy' item of the select element

value: this should be an observable and is bound to the current selection of the select element

The value binding is a very handy feature of Knockout. It removes the need to worry about IDs -- and thus optionsValue -- as you have access to the entire selected object.

So your select binding can look like:

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

Assuming you've added an observable selectedAnswer to your viewmodel.

Based on your comments, I think I now know what you're trying to do. I am going to abstract it so that it is easier to follow as your ViewModel and Objects are very complex and not easy to understand without explanation.

If you have object types, Book and Author with constructors:

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 */
}

To select a Book from a select box and have a input display bound to the Authors name, you will need the viewmodel:

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];
    }
})

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

Please note, this can get pretty inefficient over large sets of data -- look into using a better search algorithm (rather than the 'linear' used here) for your computed observable ie selectedBooksAuthor

EDIT:

Please see this fiddle for a working demonstration.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM