Reputation: 1115
I try to display a list of items in a table using Knockout JS. This is like a typical search interface, where the user types a query and the interface displays the list of results returned by the search engine. I first retrieve them from the server through an AJAX call and then try to bind them. This works for the first query very well. But when I try the second query (and subsequent queries), each result item is represented 10 times (duplicated), then for the 3rd query each item is displayed for like 30 times (and so on). Given below is the code.
<body >
<p>
<input type="search" id="skynet-query" name="q" placeholder="scientific search" autofocus />
<input type="submit" id="skynet-submit" value="Ignite!" onclick="getAttachments(document.getElementById('skynet-query').value)" />
</p>
<article data-bind="foreach: seats">
<h3>
<input data-bind="attr:{value: papers().id }" type="checkbox" name="article-to-basket" />
<a data-bind="attr:{href: papers().url }, text: papers().title"> </a>
</h3>
<address class="authors" data-bind="text: papers().authors"/>
</article>
</body>
Following is the Script part
<script>
var array = new Array();
function getAttachments(keyword) {
var request = $.ajax({
type: "GET",
datatype: "json",
url: "get-papers?q="+keyword+"&format=json&full-articles=true&kw-count=10&article-count=10&task-type=m1"
});
request.done(function (response) {
for (i=0;i<response.articles.length; i++){
array[i] = new PaperData(response.articles[i]);
}
ko.cleanNode(document);
ko.applyBindings(new ReservationsViewModel());
console.log("DONE");
});
}
function PaperData(papers) {
var self = this;
self.papers = ko.observable(papers);
}
function ReservationsViewModel() {
//var self = this;
self.seats = ko.observableArray(array);
}
</script>
Can someone help me to find what's wrong with this code?
This is what my data-model looks like
{
articles: [
{
"is-saved": false,
title: "title",
abstract: "Abstract",
date: "2005-01-01 00:00:00",
"publication-forum": "forum",
"publication-forum-type": "article",
authors: "Authors",
keywords: "keyword1, keyword2, keywordn",
id: "4f5a318e573ce53e03000015"
}
]
}
Upvotes: 0
Views: 1500
Reputation: 338386
Your approach to knockout is not quite right.
value
binding takes care of updating the view model. You never have to address an <input>
by name.onclick
. Not in Knockout and not anywhere else.array
variable that in reality should be an observable array on your view model.Here's an improved approach.
<p>
<input type="search" data-bind="value: query" placeholder="scientific search" autofocus />
<input type="submit" data-bind="click: getAttachments" />
</p>
<article data-bind="foreach: seats">
<h3>
<input data-bind="value: id, checked: selected" type="checkbox" />
<a data-bind="attr: {href: url}, text: title"></a>
</h3>
<address class="authors" data-bind="text: authors"/>
</article>
and
function PaperData(data) {
ko.utils.extend(this, data);
this.selected = ko.observable(false);
}
PaperData.create = function (data) {
return new PaperData(data);
};
function ReservationsViewModel() {
var self = this;
self.query = ko.observable();
self.seats = ko.observableArray();
self.queryParams = {
"q": self.query,
"format": "json",
"full-articles": true,
"kw-count": 10,
"article-count": 10,
"task-type": "m1"
};
self.getAttachments = function () {
$.get("get-papers", ko.toJS(self.queryParams))
.then(function (response) {
return ko.utils.arrayMap(response.articles, PaperData.create);
})
.done(self.seats);
};
}
ko.applyBindings(new ReservationsViewModel());
Notes
.then()
to transform Ajax response data on the fly..done()
to write the transformed result into an observable.queryParams
a separate property of the view model allows you to dynamically tweak other query parameters just like shown with "q"
.ko.utils.extend
to transfer properties from one object to another. Alternatively you could use the mapping plugin.Upvotes: 1