treeView.data('kendoTreeView').append(child, new_node), it cannot find the dataItem of the "new_node" in the tree view.
This occurs in kendo.web.(min).js at the "_dataSourceMove: function".
The line "referenceDataItem = destTreeview.dataItem(parentNode);" seems to give me null and thus when executing "referenceDataItem.loaded()", it will fail.
When I replaced the kendo.web.js to an older commerical version, it seems to work fine. Could I get a little insight into this? Is there new requirements that I need to add into the "new_node" so that it produces a dataItem?
Thanks in advance
15 Answers, 1 is accepted
Could you please provide more details about your scenario? We tired to reproduce the issue without success in the following jsBin -- we would appreciate it if you change it to show the problem.
All the best,Alex Gyoshev
the Telerik team

Sorry, let me add more context into this.We think this is due to the custom work arounds we made. First of all, the tree we have is dynamic and we normally load children from the server side. We are trying to mimic a search feature on our tree where it would search a text and then find it and select it on the tree.
eg. searching for '1'
go from
>Root1
>Root2
into
>Root1
> parent
> 1
>Root2
the problem is that we wish to do load on demand and only show the parents of the node we are search for. So Root1 might contain more children but we do not wish to load it. For us to do this, we currently have this code for our transport.
this part gets called when the treeNode is being expanded.
var transport = {
read:
function (options) {
if (readCausedByAdd) {
options.success([]); <----- do not do server call if appending internally
} else {
$.ajax({ <-- this wil do server call regularly do load children.
url: 'servercall',
dataType: 'json',
type: 'POST',
data: options.data
}).success(function (response) {
if (response == null)
options.success([]);
else
options.success(response);
});
}
}
The next part is to continue adding parents until we find its children. After some further investigation, we think this might be the problem but not sure how to get around it. Basically , when we are appending parents, the onDataBound will get called, and "append the child". We are thinking maybe the parent's dataItem might not have been created during onDataBound at this time??
dataBound: function (e) {
if (jQuery.isFunction(onDataBound)) onDataBound(e);
if (lastAppendedNode != null && $('#treeNode' + lastAppendedNode.id).length > 0 && lastAppendedNode.items != null) {
var possiblePlaceholder = null;
// Tracks the last added node so it can properly add all the children
if (lastAppendedNode.items.length > 1)
possiblePlaceholder = lastAppendedNode.items[1];
insertTreeNodeIntoParent(treeDiv, lastAppendedNode.items[0], possiblePlaceholder, $('#treeNode' + lastAppendedNode.id).closest(".k-item"))
// Unbind any handlers on the placeholder buttons;
// Rebind click handler to placeholder buttons.
treeDiv.find('.placeholder').parent().off('click').on('click', { "a bunch of ids"} handlePlaceholderClick);
}
Was the creation of a dataItem moved to after dataBound compared to before? If there is a better way of creating this scenario with your current system would be greatly appreciated. Please let me know if there is any more information you need to know.
Thanks
The parent node dataItem should exist in the dataBound event, because the event is fired after the datasource changes and has been processed. There is a breaking change in the HierarchicalDataSource that may be visible here, depending on how you append the child nodes. In essence, use the treeview methods instead of the .children property of the nodes. Also, please update to the latest internal build, as it has a better handling of empty arrays returned in the transport.read function. If these suggestions don't help, please send a working sample so that we can determine the root of the problem.
All the best,Alex Gyoshev
the Telerik team

So I tried the internal build (2013.1.401) but it doesn't seem to fix the problem. I also believe I am using the treeView methods append for adding new child nodes.In terms of sending a working copy, It is kind of difficult for us because most of the way we are doing things right now is on the server side. I can provide you with the js code for it though and the process, hopefully this could help.
1.) This code is the insertCode. line 09 is when I try to append the first parent node.
01.
function
insertTreeNodeIntoParent(treeView, node, new_placeholder_node, parent_elem) {
02.
readCausedByAdd =
true
;
03.
var
leafID =
null
;
04.
05.
lastAppendedNode = node;
06.
insertBeforeNode = $(parent_elem).closest(
'.k-item'
).find(
'> .k-group > .k-item > div > .k-in > .placeholder'
).closest(
'.k-item'
);
07.
if
(insertBeforeNode ==
null
|| insertBeforeNode.length === 0) {
08.
var
parent_item = $(parent_elem).closest(
'.k-item'
);
09.
treeView.data(
'kendoTreeView'
).append(node, parent_item);
10.
11.
}
2.) This is the code we use to initailize our tree ( removed some ids and params unneeded fr an example. This contains the transport and the onDataBound treeView init, gets called whenever we populate our tree. Things to note.
line 9.) this returns empty children when appending with insertIntoTreeNodeIntoParent
line 57 .) this is where it calls code insertIntoTreeNodeIntoParent again to insert the lower child. (This is where the problem occurs since we can't find the data source of the previously added node.)
01.
function
populateHierarchyTree() {
02.
// We are doing ondemand loading here, so we are using a read method that takes
03.
// the parent id (initially null) and uses that to populate the next level of the tree.
04.
// We do this in batches.
05.
var
transport = {
06.
read:
07.
function
(options) {
08.
if
(readCausedByAdd) {
09.
options.success([]);
// return empty children if we are appending
10.
}
else
{
11.
$.ajax({
12.
url:
'url'
,
13.
dataType:
'json'
,
14.
type:
'POST'
,
15.
data: options.data
16.
}).success(
function
(response) {
17.
if
(response ==
null
)
18.
options.success([]);
19.
else
20.
options.success(response);
21.
});
22.
}
23.
}
24.
};
25.
26.
// This schema ensures the use of ondemand loading.
27.
var
schema = {
28.
model: {
29.
id:
'id'
,
30.
hasChildren:
'hasChildren'
31.
}
32.
};
33.
34.
var
dataSource =
new
kendo.data.HierarchicalDataSource({
35.
transport: transport,
36.
schema: schema,
37.
error:
function
(e) {
38.
dealWithSessionTimeoutError();
39.
}
40.
});
41.
42.
treeDiv.kendoTreeView({
43.
checkboxes: checkboxes,
44.
loadOnDemand: loadOnDemand,
45.
dataSource: dataSource,
46.
dataTextField:
"text"
,
47.
dataBound:
function
(e) {
48.
if
(jQuery.isFunction(onDataBound)) onDataBound(e);
49.
50.
// Tracks the last added node so it can properly add all the children.
51.
// Note that this section will cause dataBound to be fired during insertTreeNodeIntoParent(). This is why insertTreeNodeIntoParent() only returns a ID
52.
// when it will be the last to be inserted which causes the last if statement to only execute once.
53.
if
(lastAppendedNode !=
null
&& $(
'#treeNode'
+ lastAppendedNode.id).length > 0 && lastAppendedNode.items !=
null
) {
54.
var
possiblePlaceholder =
null
;
55.
if
(lastAppendedNode.items.length > 1)
56.
possiblePlaceholder = lastAppendedNode.items[1];
57.
insertTreeNodeIntoParent(treeDiv, lastAppendedNode.items[0], possiblePlaceholder, $(
'#treeNode'
+ lastAppendedNode.id).closest(
".k-item"
))
58.
}
59.
60.
// Unbind any handlers on the placeholder buttons;
61.
// Rebind click handler to placeholder buttons.
62.
treeDiv.find(
'.placeholder'
).parent().off(
'click'
).on(
'click'
, {
'a bunch of ids'
}, handlePlaceholderClick);
63.
},
64.
select: onSelect,
65.
template: kendo.template($(
'#treeview-template'
).html()),
66.
dragAndDrop: $.isFunction(onDropEventFunction),
67.
drop: $.isFunction(onDropEventFunction) ? onDropEventFunction :
null
68.
});
69.
}
Thank you for providing this code. I have tried to reproduce the issue to no avail (here's the updated jsBin with what I tried). Looking at the code, it appears that the search functionality has entangled the datasource, databound and search functionality that you described. Theoretically, you can achieve the same effect through filtering (see sample jsBin), thus removing the need of the custom transport.read.
I hope this helps,Alex Gyoshev
the Telerik team

Sorry for the delayed reply. In terms of reproducing on jsbin. I been trying to do so myself but I have no luck with it ether. In terms of filtering, I don't think we can use it because the amount of children we can have could be enormous and those we avoid doing the server call to get the node we need. basically we have this concept of a "show more node" So we return only like.. top 10 items at most at a time. Thus we might not be able to get the node we are searching for at that time for the filter to occur. This is why we wanted to just append a new node instead of going to the server to create it.
I been looking at the jsBin you provided and I have some questions with regards to the transport. Does the transport read get called everytime we expand or do "onDatabound"?? Because from your example it seems like your read always returns node1 and node 2 as a success call but the nodes dont' get added when you expand. But when i trace through my code. I notice my transport read gets called everytime i am adding/expanding. That was why I wanted to return option.success([]) for the transport read since I want to add my own nodes and not do server call.
Could I get more information about this??
Thanks
> Does the transport read get called every time we expand or do "onDatabound"?
The function gets called when a node that has not been fetched is being expanded. In this scenario, neither Node 1 nor Node 2 have indicated that they have children, so the function is called only once for the root level. It is likely that in your scenario, you will need to call options.success([]), because the nodes will have children. Appending nodes is another case, as it will automatically expand the parent nodes, thus fetching their children through transport.read.
Note that there are some issues fixed with this functionality (i.e. options.success([]) will work) in the latest internal builds, so be sure to use the most recent build, as I previously stated.
All the best,Alex Gyoshev
the Telerik team

I tried the latest internal build but sadly there was no luck. So I did a bit of digging into the kendo.web.js file. I compared the changes from before and after. I noticed that the change on line 41296 of the latest is what's causing the problem.
so at the append function , there is a
return that._dataSourceMove(nodeData, group, parentNode, function (dataSource, model) {
function add() {
var data = dataSource.data(),
index = Math.max(data.length, 0);
if (parentNode) {
that._expanded(parentNode, true);
}
return that._insert(data, model, index);
}
Going further, during the trace , i noticed the following things.
in the _insert function
_insert: function(data, model, index) {
if (!(model instanceof kendo.data.ObservableArray)) {
if (!isArray(model)) {
model = [model];
}
} else {
// items will be converted to new Node instances
model = model.toJSON();
}
data.splice.apply(data, [ index, 0 ].concat(model));
return this.findByUid(data[index].uid);
},
Is this expected??? Once it tries to do data.splice.apply(data, [ index, 0 ].concat(model));
It will eventually give me the "loaded undefined" js error as mentioned before
I was wondering if there is something i need to add for this to work properly?
Thanks.
The splice method is used to ensure that only one change event is triggered when inserting multiple items in the dataSource. However, it circumvents some of the HierarchicalDataSource functionality, namely setting the hasChildren flag. Can you please verify if the following changed _insert method solves the problem in your scenario?
_insert: function(data, model, index) {
if (!(model instanceof kendo.data.ObservableArray)) {
if (!isArray(model)) {
model = [model];
}
} else {
// items will be converted to new Node instances
model = model.toJSON();
}
var parentNode = data.parent();
if (parentNode) {
parentNode.hasChildren = true;
parentNode._initChildren();
}
data.splice.apply(data, [ index, 0 ].concat(model));
return this.findByUid(data[index].uid);
},
Alex Gyoshev
the Telerik team

I took the new changes and tried the scenario. But it seems like the result is still the same. Once it tries to do data.splice.apply(data, [ index, 0 ].concat(model)). It will eventually still lead to the error.
here are the parameters/variables found again:
data: []
model: object containing values such as expanded = false, hasChildren = true, id etc etc
index : 0
parentNode : sub class { _events: objects, id: "some id" , parentID " some id" , nodeLevel 0, text: "parent"} <-- which seems fine.
If there is other information you need please let me know.
Thanks.

I took the new changes and tried the scenario. But it seems like the result is still the same. Once it tries to do data.splice.apply(data, [ index, 0 ].concat(model)). It will eventually still lead to the error.
here are the parameters/variables found again:
data: []
model: object containing values such as expanded = false, hasChildren = true, id etc etc
index : 0
parentNode : sub-class { _events: objects, id: "some id" , parentID " some id" , nodeLevel 0, text: "parent"}
If there is other information you need please let me know.
Thanks
We have tried quite a few scenarios based on this information, but were unable to find a problematic one. Please provide a sample that reproduces the error (even if it is not reduced to basic methods), so that we can inspect where the problem comes from.
Greetings,Alex Gyoshev
the Telerik team
We have tracked down an issue that seems related to this. Please try the latest internal build (from today) and see if it resolves the problem. If not, we might need the sample page that I requested previously.
Greetings,Alex Gyoshev
the Telerik team

The latest internal build works! Thanks !
When is the next release??
I am glad that it worked. The next official release is the service pack of Q1.2013, which should be released in mid-May. You would need to wait for it if you need a CDN version of the scripts; otherwise, you can use the internal build, since it is continuously tested.
All the best,Alex Gyoshev
the Telerik team