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

Performance issue with grid reorder using drag and drop

3 Answers 314 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Ondrej
Top achievements
Rank 1
Ondrej asked on 02 Oct 2018, 01:53 PM

Hi there,

I have below code:

dropsGrid.table.kendoSortable({<br>            filter: ">tbody >tr",<br>            placeholder: $('<tr class="placeholder"><td colspan="11">Drop Here!</td></tr>'),<br>            hint: function (element) {<br>                var width = $('#Drops').width();<br>                var table = $('<table style="width: ' + width + 'px;" class="k-grid k-widget"></table>'),<br>                    hint;<br><br>                table.append($('#Drops .k-state-selected').clone());<br>                table.css("opacity", 0.7);<br><br>                return table;<br>            },<br>            start: function (e) {<br>                if ($('#Drops .k-state-selected').length == 0) {<br>                    e.preventDefault();<br>                } else {<br>                    $('#Drops .k-state-selected').hide();<br>                }<br>            },<br>            end: function (e) {<br>                $('#Drops .k-state-selected').show();<br>            },<br>            change: function (e) {<br>                try {<br>                    isDropInProgress = true;<br><br>                    var items = this.element.find('.k-state-selected').not(e.item);<br>                    for (var i = 0; i < items.length; i++) {<br>                        console.log(items[i]);<br>                        $(items[i]).insertBefore(e.item).show();<br>                    }<br><br>                    var selectedSequence = $(e.item[0]).find('.row-number').text();<br>                    for (var i = 0; i < items.length; i++) {<br>                        var sequence = $(items[i]).find('.row-number').text();<br>                        if (Number(sequence) > Number(selectedSequence)) { <br>                            $(e.item).insertBefore(items[i]);<br>                            break;<br>                        }<br>                    }<br><br>                    var rows = $(dropsGrid.tbody[0]).find('tr');<br>                    for (var i = 0; i < rows.length; i++) {<br>                        var uid = $(rows[i]).data('uid');<br>                        var drops = dropsGrid.dataSource.data();<br>                        var dataItem = null;<br>                        for (var j = 0; j < drops.length; j++) {<br>                            if (drops[j].uid == uid) {<br>                                dataItem = drops[j];<br>                                break;<br>                            }<br>                        }<br>                        if (dataItem != null) {<br>                            dropsGrid.dataSource.remove(dataItem);<br>                            dropsGrid.dataSource.insert(i, dataItem);<br>                        }<br>                    }<br><br>                } finally {<br>                    isDropInProgress = false;<br>                    isDirty = true;<br>                    onDataBound();<br>                    refreshMap(dropsGrid.dataSource._data);<br>                }<br>            },<br>            cancel: function (e) {<br>                $('#Drops .k-state-selected').show();<br>            }<br>        }).data("kendoSortable");

With this code the user can reorder rows on a grid using drag and drop. Also "refreshMap(dropsGrid.dataSource._data);" updates the pins on a google map when the user changes the order of the rows.

Everything works as expected, but if the grid contains, lets say 80 rows and you are trying to move 3 rows at a time the performance takes a significant hit. I.e It takes between 9 to 10 seconds for the rows to drop in place.

I did some debugging around it and found that the issue is with the chunk of code below:

var rows = $(dropsGrid.tbody[0]).find('tr');<br>                    for (var i = 0; i < rows.length; i++) {<br>                        var uid = $(rows[i]).data('uid');<br>                        var drops = dropsGrid.dataSource.data();<br>                        var dataItem = null;<br>                        for (var j = 0; j < drops.length; j++) {<br>                            if (drops[j].uid == uid) {<br>                                dataItem = drops[j];<br>                                break;<br>                            }<br>                        }<br>                        if (dataItem != null) {<br>                            dropsGrid.dataSource.remove(dataItem);<br>                            dropsGrid.dataSource.insert(i, dataItem);<br>                        }<br>                    }

I tried for the past couple of days to replace the nested for loops but without a complete successful result.

I got as far as getting the single selection to work synchronously with the map by using below code:

<code style="color: #069;font-weight: bold;">var</code> <code style="color: #000;">skip = 0,<br>                    oldIndex = e.oldIndex + skip,<br>                    newIndex = e.newIndex + skip,<br>                    </code><code style="color: #008200;">data = dropsGrid.dataSource.data(),<br>                    dataItem = dropsGrid.dataSource.getByUid(e.item.data("uid"));<br><br>                console.log("------" + e.newIndex + " " + newIndex + " " + skip + " - " + e.oldIndex + " " + oldIndex + " " + skip);<br><br>                dropsGrid.dataSource.remove(dataItem);<br>                dropsGrid.dataSource.insert(newIndex, dataItem);</code>

but I am loosing the multiple selection functionality.

By using below code:

start: function (e) {<br>                if ($('.k-state-selected').length === 0) {<br>                    e.preventDefault();<br>                } else {<br>                    $('.k-state-selected').hide();<br>                }<br>            },<br>            end: function (e) {<br>               <br>                $('.k-state-selected').show();<br>            },<br>            change: function (e) {<br>                try {<br>                    isDropInProgress = true;<br><br>                    console.time("whole drop");<br><br>                    var items = this.element.find('.k-state-selected').not(e.item);<br>                    for (var i = 0; i < items.length; i++) {<br>                        console.log(items[i]);<br>                        $(items[i]).insertBefore(e.item).show();<br>                    }<br><br>                    var selectedSequence = $(e.item[0]).find('.row-number').text();<br>                    for (var i = 0; i < items.length; i++) {<br>                        var sequence = $(items[i]).find('.row-number').text();<br>                        if (Number(sequence) > Number(selectedSequence)) { <br>                            $(e.item).insertBefore(items[i]);<br>                            break;<br>                        }<br>                    }<br><br>                    $('.k-state-selected').removeClass("state-selected");<br><br>                    console.timeEnd("whole drop");<br>                } finally {<br>                    isDropInProgress = false;<br>                    isDirty = true;<br>                    onDataBound();<br>                    refreshMap(dropsGrid.dataSource._data);<br>                }<br>            },<br>            cancel: function (e) {<br>                $('.k-state-selected').show();<br>            } <br>        }).data("kendoSortable");<br><br>        testDrop.draggable.userEvents.bind("tap", function (e) {<br>            if (e.event.ctrlKey) {<br>                e.target.toggleClass("state-selected");<br>            } else {<br>                $('.k-state-selected').removeClass("state-selected");<br>                e.target.addClass("state-selected");<br>            }<br>        });

multiple and single selection works but the pins on the map are not updating anymore.

Another issue with the above approach is that when I move, lets say, two rows from position 1 and 2 under row 5 I am getting following behavior:

- the 2 rows that I moved are selected (expected behavior)

- the rows that replaced position 1 and 2 are also selected (unwanted behavior, as this creates confusion)

As I spent couple of days already on this issue I was wondering if you can, please, help with an example or some guidance in order to resolve the issue.

I am looking forward to hearing from you!

Thank you

3 Answers, 1 is accepted

Sort by
0
Stamo Gochev
Telerik team
answered on 05 Oct 2018, 09:59 AM
Hi Ondrej,

Can you send a runable Dojo example, which demonstrates the issue, so I can make a performance review on the called methods? This is necessary as it is hard to tell which part of the functionality might cause the slow behavior, especially when multiple widgets are used together.

In addition, the grid should work fast enough with a lot more than 80 rows, so I suppose there is a place in the custom implementation that can be improved in order to handle larger amount of data.

Regards,
Stamo Gochev
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Ondrej
Top achievements
Rank 1
answered on 10 Oct 2018, 01:39 PM

Hi Stamo,

I have managed to break the nested "for" loop but I am still having issues with the time it takes for the drop to happen. I identified that the issue is when I am sorting the rows during the drop. More exactly the longest operation is this:

if (dataItem != null) {
       dropsGrid.dataSource.remove(dataItem);
       dropsGrid.dataSource.insert(i, dataItem);
}

Here you can find a dojo: http://dojo.telerik.com/uXiyUjiS

Can you please let me know if there is another way of reshuffling the data in order to reflect the new position of the rows?

Thank you!

0
Stamo Gochev
Telerik team
answered on 15 Oct 2018, 07:05 AM
Hello,

Thanks for sending me a runable Dojo example, which helped me investigate the issue.

After making a performance review, it turned out that the poor performance is actually expected due to the fact that for each step of the loop in the "change" handler of the Sortable, the grid is re-rendered one time due to the following line:
grid.dataSource.remove(dataItem);
and once more for:
grid.dataSource.insert(index, dataItem)
Here is a modified version of the Dojo example, which adds a counter for the "dataBound" event, so that the above can be tested:

http://dojo.telerik.com/uXiyUjiS/2

Dragging a single row with a grid with 10 items results in the grid being re-rendered 20 times. This will definitely yield bad performance, especially if there are more items and that is why the current implementation approach cannot be as fast as expected.

Here is a list of things that I can recommend:

1. I am not sure why every row in the grid should be removed and inserted at a specific position - can this be skipped? If not, I can suggest preparing the new data in the specific order manually beforehand and changing the "data" of the DataSource at once, which should improve the things to a certain point (still not ideal)

https://docs.telerik.com/kendo-ui/api/javascript/data/datasource/methods/data

2. Replace only two rows (instead of all the rows) as demonstrated in the help topic about reordering rows:

https://docs.telerik.com/kendo-ui/controls/interactivity/sortable/how-to/use-sortable-grid

3. Reduce the page size.

Regards,
Stamo Gochev
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Tags
Grid
Asked by
Ondrej
Top achievements
Rank 1
Answers by
Stamo Gochev
Telerik team
Ondrej
Top achievements
Rank 1
Share this question
or