This is a migrated thread and some comments may be shown as answers.

Cannot Seem to find dataItem of an appended node on TreeView.

15 Answers 892 Views
TreeView
This is a migrated thread and some comments may be shown as answers.
Paul
Top achievements
Rank 1
Paul asked on 28 Mar 2013, 06:51 PM
Hi, After updating to .2013.1.319. I been having an internal js error on kendo.web.js. It seems that when I append a "new_node" into the tree view and try to a "child" to the new node using 

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

Sort by
0
Alex Gyoshev
Telerik team
answered on 29 Mar 2013, 01:51 PM
Hello Paul,

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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Paul
Top achievements
Rank 1
answered on 01 Apr 2013, 03:38 PM
Hi Alex,

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
0
Alex Gyoshev
Telerik team
answered on 02 Apr 2013, 07:20 AM
Hello Paul,

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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Paul
Top achievements
Rank 1
answered on 03 Apr 2013, 06:21 PM
Hi Alex, Sorry for the delay.
 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.}
0
Alex Gyoshev
Telerik team
answered on 05 Apr 2013, 09:57 AM
Hello Paul,

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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Paul
Top achievements
Rank 1
answered on 09 Apr 2013, 06:23 PM
Hi Alex,

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
0
Alex Gyoshev
Telerik team
answered on 10 Apr 2013, 10:58 AM
Hello Paul,

> 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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Paul
Top achievements
Rank 1
answered on 10 Apr 2013, 06:01 PM
hi Alex,

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);
                }
So there was a change it seems that does this now that._insert(data, model, index); while before that it did this.  return dataSource.add(model); . When i replaced the lines it seems to work perfectly again.
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);
        },
the data is an empty array, model is an object with the proper information, and index is 0. 
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.




0
Alex Gyoshev
Telerik team
answered on 11 Apr 2013, 03:21 PM
Hello Paul,

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);
},

All the best,
Alex Gyoshev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Paul
Top achievements
Rank 1
answered on 11 Apr 2013, 05:28 PM
Hi Alex,

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.


0
Paul
Top achievements
Rank 1
answered on 11 Apr 2013, 09:14 PM
Hi Alex,
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
0
Alex Gyoshev
Telerik team
answered on 15 Apr 2013, 11:08 AM
Hello Paul,

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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Accepted
Alex Gyoshev
Telerik team
answered on 15 Apr 2013, 02:22 PM
Hello Paul,

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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Paul
Top achievements
Rank 1
answered on 15 Apr 2013, 09:31 PM
Hi Alex,

The latest internal build works! Thanks !
When is the next release??
0
Alex Gyoshev
Telerik team
answered on 16 Apr 2013, 07:26 AM
Hello Paul,

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
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
Tags
TreeView
Asked by
Paul
Top achievements
Rank 1
Answers by
Alex Gyoshev
Telerik team
Paul
Top achievements
Rank 1
Share this question
or