Search code examples
javascriptknockout.jsundefinedclient-sideko.observablearray

Knockout: Can't get specific value from observableArray


I created array with data about some music albums in viewmodel. I can use this array in foreach loop (for example) but I can't display only one value from this array.

My viewmodel:

function CD(data) {
    this.Author = ko.observable(data.Author);
    this.Title = ko.observable(data.Title);
    this.Price = ko.observable(data.Price);
    this.Label = ko.observable(data.Label);
    this.Id = ko.observable(data.Id);
}

function NewsListViewModel() {
    var self = this;
    self.newsList = ko.observableArray([]);

    $.getJSON("/music/news", function (allData) {
        var mapped = $.map(allData, function (item) { return new CD(item) });
        self.newsList(mapped);
    });

    self.oneObject = ko.computed(function () {
        return self.newsList()[0].Title;
    });
}

$(document).ready(function () {
    ko.applyBindings(new NewsListViewModel());
});

And how I used it in html:

<ul data-bind="foreach: newsList">
     <li data-bind="text: Title"></li>
     <li data-bind="text: Author"></li>
     <li data-bind="text: Price"></li>
     <li data-bind="text: Label"></li>
</ul>

This works perfectly. But if I trying additionally display one value:

<ul data-bind="foreach: newsList">
     <li data-bind="text: Title"></li>
     <li data-bind="text: Author"></li>
     <li data-bind="text: Price"></li>
     <li data-bind="text: Label"></li>
</ul>
...
<p data-bind="text: oneObject"</p>

it dosen't working, firebug thrown error:

Type error: self.newsList()[0].Title is undefined

Why I cannot get one specific value but I can display whole list?

PS. Code <p data-bind="text: newsList()[0].Title"</p> didn't works too.


Solution

  • You get the error because $.getJSON is asynchronous.

    So when your computed is declared your items are not necessary ready in the newsList so there is no first element what you could access.

    So you need to check in your computed that your newsList is not empty (anyway your server could also return an empty list so it is always good to have this check) and only then access the first element:

    self.oneObject = ko.computed(function () {
        if (self.newsList().length > 0)
            return self.newsList()[0].Title();
        return 'no title yet';
    });
    

    Note: you need also write .Title() to get its value because Title is an observable.