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!
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 theTweetsViewModel
constructor again (in thesetTimeout
call) thethis
reference will point to thewindow
object.Even if you get the
this
reference to point to the right object (which is possible using theapply
method that functions have, but that's beside the point) you would still not be updating the observableArray, since you'd be setting theself.tweets
variable to a new observableArray on the lineAll 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:
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);
.