Farhad-Taran
Farhad-Taran

Reputation: 6540

how to bind to items within an observable array in knockout?

I have the following observable array:

self.Profiles =ko.observableArray( ko.utils.arrayMap(initialData, function (profile) {
                return {
                    StartDate : formatDateOnly(profile.StartDate),
                    EndDate : formatDateOnly(profile.EndDate),
                    ProfileID :profile.ID,
                    ProfileName : profile.Name,
                    ProjectName : profile.ProjectName,
                    ReadingListID : profile.ReadingListID,
                    ReadingListName : profile.ReadingListName

                };
            }));

I want to bind a dropdown to the profiles to show the profile names, if the value of the drop down changes then I want to update span elements with the new corresponding values to the selected profileID.

<table id="readingListApplyToProfile" class="fullWidthTable">
        <tr>
            <td>
                Profile:
            </td>
            <td>
                <select id="cboProfile" name="cboProfile" data-bind="options: Profiles, optionsText: 'ProfileName', 'optionsCaption': 'Select profile...', optionsValue:'ProfileID'"></select>
            </td>
        </tr>
        <tr>
            <td>
                End Date:
            </td>
            <td>
                <span data-bind="'text':EndDate"></span>
            </td>
        </tr>
    </table>

I cannot get the spans to update because the span elements are unaware of the dropdowns value, can anyone help me please. I am totally lost.

Upvotes: 1

Views: 1621

Answers (2)

jonhopkins
jonhopkins

Reputation: 3842

I had the idea of a computedObservable that takes in a ProfileID and outputs the Profile corresponding to that ID, and then binding the spans to the various properties of the outputted object. Surprisingly, it worked. I was messing around with Sujesh Arukil's fiddle to try my idea so the model is very similar.

Working example showing multiple spans: http://jsfiddle.net/jonhopkins/fgZNQ/2/

The Model:

var myViewModel = function()
{
    var self = this;
        self.Profiles =ko.observableArray([{
                    StartDate : '02/01/2012',
                    EndDate : '01/01/2013',
                    ProfileID :10,
                    ProfileName : 'Some Name',
                    ProjectName : 'Some Project',
                    ReadingListID : 100,
                    ReadingListName : 'Some List',
                },
                {
                    StartDate : '12/01/2012',
                    EndDate : '02/27/2013',
                    ProfileID :12,
                    ProfileName : 'New Name',
                    ProjectName : 'New Project',
                    ReadingListID : 200,
                }]);

    self.selectedProfileId = ko.observable();

    self.getProfile = ko.computed({
        read: function() {
            for (var i = 0; i < self.Profiles().length; i++) {
                if (self.Profiles()[i].ProfileID == self.selectedProfileId()) {
                    return self.Profiles()[i];
                }
            }
            // in case there was no match, output a blank Profile
            return [{
                    StartDate : '',
                    EndDate : '',
                    ProfileID : '',
                    ProfileName : '',
                    ProjectName : '',
                    ReadingListID : '',
                    ReadingListName : ''
            }];
        },
        write: function(value) {
            self.selectedProfileId(value);
        },
        owner: self
    });
}

ko.applyBindings(new myViewModel());

EDIT: Sujesh suggested a much better version of the computedObservable.

self.getProfile = ko.computed(function(){
    var profile = ko.utils.arrayFirst(self.Profiles(), function(prof){
        return prof.ProfileID == self.selectedProfileId();
    });

    return profile || {};
});

The HTML:

<table id="readingListApplyToProfile" class="fullWidthTable">
    <tr>
        <td>
            Profile:
        </td>
        <td>
            <select id="cboProfile" name="cboProfile" data-bind="options: Profiles, optionsText: 'ProfileName', 'optionsCaption': 'Select profile...', optionsValue:'ProfileID', value: getProfile "></select>
        </td>
    </tr>
    <tr>
        <td>
            End Date:
        </td>
        <td>
            <span data-bind="text: getProfile().ProfileName"></span>
        </td>
    </tr>
</table>

Upvotes: 2

Sujesh Arukil
Sujesh Arukil

Reputation: 2469

What you are missing is a value binding on the drop down list. Here is a fiddle I created.

http://jsfiddle.net/sujesharukil/sBZvb/1/

<select id="cboProfile" name="cboProfile" data-bind="options: Profiles, optionsText: 'ProfileName', 'optionsCaption': 'Select profile...', optionsValue:'ProfileID', value: selectedProfileId ">

And here is the viewmodel

var myViewModel = function()
{
    var self = this;
        this.Profiles =ko.observableArray([{
                    StartDate : '02/01/2012',
                    EndDate : '01/01/2013',
                    ProfileID :10,
                    ProfileName : 'Some Name',
                    ProjectName : 'Some Project',
                    ReadingListID : 100,
                    ReadingListName : 'Some List',
                },
                {
                    StartDate : '12/01/2012',
                    EndDate : '02/27/2013',
                    ProfileID :12,
                    ProfileName : 'New Name',
                    ProjectName : 'New Project',
                    ReadingListID : 200,
                }]);
    this.selectedProfileId = ko.observable({}); //this stores the selected id

}

ko.applyBindings(new myViewModel());

Hope that helps.

Suj

Upvotes: 3

Related Questions