最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to iterate an Knockout ObservableArray? (length 0) - Stack Overflow

programmeradmin1浏览0评论

I'm new to knockout.js (and this is also my first stackoverflow post) and I'm now facing the following problem.

I'm not able to bind the data from web api to a ko.observablearray. Why is the length of this Announcements ko.observablearray always 0? The code works fine with client side data (by adding new announcements)..

Here's the JS-code:

var AnnouncementModel = function () {
    var self = this;

    self.AnnouncementText = ko.observable();
    self.AllDepartmentsBool = ko.observable();
    self.Editable = ko.observable(false);

    self.Add = function () {
        viewModel.Announcements.push(self);
        viewModel.AnnouncementToEdit(new AnnouncementModel());
    };

    self.Delete = function () {
        ajaxHelper(announcementsUri + self.ID, 'DELETE').done(
        viewModel.Announcements.remove(self));
    };

    self.Edit = function () {
        self.Editable(!self.Editable());
    };
}

//The ViewModel
function AnnouncementsViewModel() {
    var self = this;

    self.InitialData = ko.observableArray();
    self.Announcements = ko.observableArray();
    self.AnnouncementToEdit = ko.observable(new AnnouncementModel());
    self.error = ko.observable();

    function getAllAnnouncements() {
        ajaxHelper(announcementsUri, 'GET').done(function(data) {
            self.InitialData(data);        
        });
    };

    getAllAnnouncements();
};

var viewModel = new AnnouncementsViewModel();
ko.applyBindings(viewModel, document.getElementById("announcements-container"));

function createAnnouncement(announcementDto) {
    var announcement = new AnnouncementModel();
    announcement.AnnouncementText = ko.observable(announcementDto.AnnouncementText);
    announcement.AllDepartmentsBool = ko.observable(announcementDto.AllDepartmentsBool);
    announcement.Editable = ko.observable(false);

    return announcement;
}

var length = viewModel.InitialData.length;
for (i = 0; i < length; i++) {
    var newAnnouncement = createAnnouncement(InitialData[i]);
    viewModel.Announcements.push(newAnnouncement);
}

The HTML:

<div id="announcements-container" style="display: inline-block; float: right">
    <ul id="announcements-list" class="newsticker" data-bind="foreach: Announcements">
        <li>
            <span data-bind="html: AnnouncementText"></span>
        </li>
    </ul>
    @Html.Partial("_AnnouncementsModal")
</div>

The InitialData gets populated from the api as it should:


GOT IT WORKING! :

Thanks for the quick answers. I got the code working by iterating the data with .forEach(). Another problem was that the initialData didn't get populated in it's current scope so I edited the getAnnouncements function to work like this :

function getAllAnnouncements() {
    ajaxHelper(announcementsUri, 'GET').done(function(data) {
        data.forEach(function (entry) {
            var newAnnouncement = createAnnouncement(entry);
            self.Announcements.push(newAnnouncement);
        });
    });
};

I'm new to knockout.js (and this is also my first stackoverflow post) and I'm now facing the following problem.

I'm not able to bind the data from web api to a ko.observablearray. Why is the length of this Announcements ko.observablearray always 0? The code works fine with client side data (by adding new announcements)..

Here's the JS-code:

var AnnouncementModel = function () {
    var self = this;

    self.AnnouncementText = ko.observable();
    self.AllDepartmentsBool = ko.observable();
    self.Editable = ko.observable(false);

    self.Add = function () {
        viewModel.Announcements.push(self);
        viewModel.AnnouncementToEdit(new AnnouncementModel());
    };

    self.Delete = function () {
        ajaxHelper(announcementsUri + self.ID, 'DELETE').done(
        viewModel.Announcements.remove(self));
    };

    self.Edit = function () {
        self.Editable(!self.Editable());
    };
}

//The ViewModel
function AnnouncementsViewModel() {
    var self = this;

    self.InitialData = ko.observableArray();
    self.Announcements = ko.observableArray();
    self.AnnouncementToEdit = ko.observable(new AnnouncementModel());
    self.error = ko.observable();

    function getAllAnnouncements() {
        ajaxHelper(announcementsUri, 'GET').done(function(data) {
            self.InitialData(data);        
        });
    };

    getAllAnnouncements();
};

var viewModel = new AnnouncementsViewModel();
ko.applyBindings(viewModel, document.getElementById("announcements-container"));

function createAnnouncement(announcementDto) {
    var announcement = new AnnouncementModel();
    announcement.AnnouncementText = ko.observable(announcementDto.AnnouncementText);
    announcement.AllDepartmentsBool = ko.observable(announcementDto.AllDepartmentsBool);
    announcement.Editable = ko.observable(false);

    return announcement;
}

var length = viewModel.InitialData.length;
for (i = 0; i < length; i++) {
    var newAnnouncement = createAnnouncement(InitialData[i]);
    viewModel.Announcements.push(newAnnouncement);
}

The HTML:

<div id="announcements-container" style="display: inline-block; float: right">
    <ul id="announcements-list" class="newsticker" data-bind="foreach: Announcements">
        <li>
            <span data-bind="html: AnnouncementText"></span>
        </li>
    </ul>
    @Html.Partial("_AnnouncementsModal")
</div>

The InitialData gets populated from the api as it should:


GOT IT WORKING! :

Thanks for the quick answers. I got the code working by iterating the data with .forEach(). Another problem was that the initialData didn't get populated in it's current scope so I edited the getAnnouncements function to work like this :

function getAllAnnouncements() {
    ajaxHelper(announcementsUri, 'GET').done(function(data) {
        data.forEach(function (entry) {
            var newAnnouncement = createAnnouncement(entry);
            self.Announcements.push(newAnnouncement);
        });
    });
};
Share Improve this question edited Dec 29, 2015 at 14:13 Markus Walden asked Dec 29, 2015 at 13:17 Markus WaldenMarkus Walden 531 silver badge6 bronze badges 0
Add a ment  | 

2 Answers 2

Reset to default 7

This line is the likely culprit:

var length = viewModel.InitialData.length;

Remember that InitialData is a function. Functions have a length (it's their "arity", the number of formal arguments they have), but the observable function for an observable array's length isn't the array's length..

You probably wanted the length of the array inside it:

var length = viewModel.InitialData().length;
// -------------------------------^^

Your various calls to push on observable arrays work even though length doesn't because Knockout provides push (and several other things) on the observable array function, as James points out.


Similarly, this line:

var newAnnouncement = createAnnouncement(InitialData[i]);

probably wants to be using the array as well (and is missing viewModel. in front of InitialData).

So that whole section probably wants to be refactored a bit:

viewModel.InitialData().forEach(function(entry) {
    var newAnnouncement = createAnnouncement(entry);
    viewModel.Announcements.push(newAnnouncement);
});

or without forEach (but really, it's nearly 2016, and it's shimmable on obsolete browsers);

var data = viewModel.InitialData();
for (var i = 0; i < data.length; ++i) {
    var newAnnouncement = createAnnouncement(data[i]);
    viewModel.Announcements.push(newAnnouncement);
}

Side note: Your code (at least as it is in the question) was also falling prey to The Horror of Implicit Globals by not declaring the i that you use in that for loop. I've added a var above, but this is another reason for using forEach to loop through arrays.

You can also use EcmaScript 6 style enumeration as follows:

viewModel.InitialData().forEach(item => {
    let newAnnouncement = createAnnouncement(item);
    viewModel.Announcements.push(newAnnouncement);
});
发布评论

评论列表(0)

  1. 暂无评论