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

javascript - Knockout ObservableArray not updating HTML Foreach - Stack Overflow

programmeradmin1浏览0评论

So I've got an observablearray that works fine, but the UI doesn't update. I've read lots of people running into this TYPE of issue, but I'm not seeing it.

So the HTML is

            <tbody data-bind="foreach: tweets">
            <tr>
                <td>
                    <span data-bind="visible: created_at" class="label label-success">Yup</span>
                </td>
                <td><p><b data-bind="text: screen_name"></b></p></td>
                <td><p><b data-bind="text: id"></b></p></td>
                <td><p data-bind="text: text"></p></td>
                <td><p data-bind="text: created_at"></p></td>
            </tr>
            </tbody>

And the Javascript is a function that calls an API and builds an array from it.

<script type="text/javascript">
        function TweetsViewModel() {
            var self = this;
            self.tasksURI = 'http://localhost:8000/api/v1/tweet/';
            self.tweets = ko.observableArray();

            self.ajax = function(uri, method, data) {
                var request = {
                    url: uri,
                    type: method,
                    contentType: "application/json",
                    Accept: "application/json",
                    cache: false,
                    dataType: 'json',
                    data: JSON.stringify(data)
                };
                return $.ajax(request);
            }

            self.ajax(self.tasksURI, 'GET').done(function(data) {
                for (var i = 0; i < data.objects.length; i++) {
                    var tweet = this;
                    tweet.created_at = ko.observable(data.objects[i].created_at)
                    tweet.text = ko.observable(data.objects[i].text)
                    tweet.id = ko.observable(data.objects[i].id)
                    tweet.screen_name = ko.observable(data.objects[i].twitteruser.screen_name)
                    self.tweets.push(tweet)
                }
            });
            setTimeout(TweetsViewModel, 10000)
        }
        ko.applyBindings(new TweetsViewModel());
    </script>

Just for testing purposes, I'm using the setTimeout to just rerun the call to the API and update the array. The Array gets updated properly, but the UI doesn't. I apologize for my ignorance (been a backend dev for a longtime, this is my first run at Javascript in 10 years). Any help much appreciated!

So I've got an observablearray that works fine, but the UI doesn't update. I've read lots of people running into this TYPE of issue, but I'm not seeing it.

So the HTML is

            <tbody data-bind="foreach: tweets">
            <tr>
                <td>
                    <span data-bind="visible: created_at" class="label label-success">Yup</span>
                </td>
                <td><p><b data-bind="text: screen_name"></b></p></td>
                <td><p><b data-bind="text: id"></b></p></td>
                <td><p data-bind="text: text"></p></td>
                <td><p data-bind="text: created_at"></p></td>
            </tr>
            </tbody>

And the Javascript is a function that calls an API and builds an array from it.

<script type="text/javascript">
        function TweetsViewModel() {
            var self = this;
            self.tasksURI = 'http://localhost:8000/api/v1/tweet/';
            self.tweets = ko.observableArray();

            self.ajax = function(uri, method, data) {
                var request = {
                    url: uri,
                    type: method,
                    contentType: "application/json",
                    Accept: "application/json",
                    cache: false,
                    dataType: 'json',
                    data: JSON.stringify(data)
                };
                return $.ajax(request);
            }

            self.ajax(self.tasksURI, 'GET').done(function(data) {
                for (var i = 0; i < data.objects.length; i++) {
                    var tweet = this;
                    tweet.created_at = ko.observable(data.objects[i].created_at)
                    tweet.text = ko.observable(data.objects[i].text)
                    tweet.id = ko.observable(data.objects[i].id)
                    tweet.screen_name = ko.observable(data.objects[i].twitteruser.screen_name)
                    self.tweets.push(tweet)
                }
            });
            setTimeout(TweetsViewModel, 10000)
        }
        ko.applyBindings(new TweetsViewModel());
    </script>

Just for testing purposes, I'm using the setTimeout to just rerun the call to the API and update the array. The Array gets updated properly, but the UI doesn't. I apologize for my ignorance (been a backend dev for a longtime, this is my first run at Javascript in 10 years). Any help much appreciated!

Share Improve this question asked Feb 15, 2014 at 21:41 user3314493user3314493 931 silver badge3 bronze badges 0
Add a ment  | 

1 Answer 1

Reset to default 3

The problem is that you're never updating the observableArray.

To start with, you're calling the constructor again, but the this reference is probably not pointing to the object you think. When you call the TweetsViewModel constructor again (in the setTimeout call) the this reference will point to the window object.

Even if you get the this reference to point to the right object (which is possible using the apply method that functions have, but that's beside the point) you would still not be updating the observableArray, since you'd be setting the self.tweets variable to a new observableArray on the line

self.tweets = ko.observableArray();

All subscriptions, e.g. the UI DOM elements, would still be subscribed to the old observableArray since they would not know that the variable has changed.

So what you should probably do is to create a function which performs reload of the data, like the following:

self.reloadData = function(){
    self.ajax(self.tasksURI, 'GET').done(function(data) {
        for (var i = 0; i < data.objects.length; i++) {
            // Important: Create a new tweet object instead of using 'this' here.
            var tweet = {};
            tweet.created_at = ko.observable(data.objects[i].created_at)
            tweet.text = ko.observable(data.objects[i].text)
            tweet.id = ko.observable(data.objects[i].id)
            tweet.screen_name = ko.observable(data.objects[i].twitteruser.screen_name)
            self.tweets.push(tweet)
        }
    });
}
setTimeout(self.reloadData, 10000);

Also, be aware that this would always add tweets to the observableArray (never clear it) and also it would cause the subscriptions to the observableArray to fire once per tweet added. If you would want to replace the array you should probably create a replacement array, push tweets into that array and finally replace the array in the observableArray by doing self.tweets(replacementArray);.

发布评论

评论列表(0)

  1. 暂无评论