The title might be longish, but I couldn't be more concise. Yet the problem is rather simple and semi-popular: let's suppose we have a `kendoListView` with some data, bound to a view model (and thus data-initialized) like this:
HTML:
<
ul
data-role
=
"listview"
data-bind
=
"source: dataSource"
data-template
=
"temp"
></
ul
>
<
script
id
=
"temp"
type
=
"text/x-kendo-template"
>
<
li
>#=id# #=name#</
li
>
</
script
>
JavaScript:
var
dataSource =
new
kendo.data.DataSource({
data: [
{id: 1, name:
'John Clausky'
},
{id: 2, name:
'Filippo Divoni'
},
{id: 3, name:
'Foo Bar'
},
]
});
var
viewModel = kendo.observable({
dataSource: dataSource
});
kendo.bind($(
"body"
), viewModel);
When adding a new item to the dataSource, kendoListView is refreshed correctly. Same with filtering the dataSource. However, changing an existing item in the dataSource causes no visible changes in the widget. Here's the demonstration: http://dojo.telerik.com/AgEhi/2
When researching the problem, I've found plenty of answers on the internet, that the kendoListView widget simply has to be refreshed manually with `refresh() function. But why the widget is properly refreshed when adding a new data item then? Moreover, when kendoListView was not bound via view model, everything works just fine (demonstration: http://dojo.telerik.com/EgidE).
I've found the problematic moment in the source code. It turns out the refresh() function is called always when manipulating the ​underlying DataSource, however it's not always refreshing the widget itself (kendo.listview.js:184, at least in the copy I have; I commented out the parts which I find irrelevant to the problem):
01.
refresh:
function
(e){
02.
03.
// ...
04.
05.
if
(e.action ===
"itemchange"
) {
06.
if
(!that._hasBindingTarget() && !that.editable) {
07.
data = e.items[0];
08.
item = that.items().filter(
"["
+ kendo.attr(
"uid"
) +
"="
+ data.uid +
"]"
);
09.
10.
if
(item.length > 0) {
11.
idx = item.index();
12.
13.
that.angular(
"cleanup"
,
function
() {
14.
return
{ elements: [ item ]};
15.
});
16.
17.
item.replaceWith(template(data));
18.
item = that.items().eq(idx);
19.
item.attr(kendo.attr(
"uid"
), data.uid);
20.
21.
that.angular(
"compile"
,
function
() {
22.
return
{ elements: [ item ], data: [ { dataItem: data } ]};
23.
});
24.
25.
that.trigger(
"itemChange"
, {
26.
item: item,
27.
data: data
28.
});
29.
}
30.
}
31.
32.
return
;
33.
}
34.
35.
// ...
36.
37.
for
(idx = 0, length = view.length; idx < length; idx++) {
38.
if
(idx % 2) {
39.
html += altTemplate(view[idx]);
40.
}
else
{
41.
html += template(view[idx]);
42.
}
43.
}
44.
45.
that.element.html(html);
46.
47.
// ...
48.
},
The problem is in the first if statement (line ​5). When a data item is modified, the condition is met (a.action is equal "itemchange"). Then that._hasBindingTarget() evaluates to true, when the widget is data-initialized (is using a view model) and thus skips straight to the return statement. This is also why the item is updated when no view models are involved (line ​7) and why the widget is rebuilt when adding new data item or filtering the data (line 37).
As suspected, modifying the first if statement to always refresh the data, miraculously solves the problem, despite everyone saying it has to be done manually (and thus calling the refresh() function twice actually, which also solves the problem, "but" ...).
What was the reasoning for such blockade? Can I safely modify the if statement to solve my problem? What other features can potentially fail afterwards? All information about this will be invaluable.
Cheers, Thomas