View Model with an Array Property - Performance Issue (maybe a bug).

4 posts, 0 answers
  1. Grant Parker
    Grant Parker avatar
    2 posts
    Member since:
    Oct 2009

    Posted 26 Nov 2013 Link to this post

    If you have a View Model that has a property that is an array and you bind it via "kendo.bind", whenever you change any input bound to the array, it refreshes/rebinds the ENTIRE ARRAY.  Is this by design or a bug?  It is definitely a performance issue when you have a big array.  Below is a sample project I made to demonstrate.  If you type a character in any of the inputs, it will auto-tab to the next input.  However, due to performance issue, it takes a long time for it to do so.  I am logging the "get" event to the console, and you will see that after you type into one of the inputs, every property of every item in the array is gotten again; hence the horrible performance.

    ================================================================
    @{
    ViewBag.Title = "Test";
    }

    <div id="placeholder"></div>

    <script type="text/x-kendo-template" id="template">
    <table id="scorecard">
    <caption>#= TeamName #</caption>
    # for (var i = 0; i < Scores.length; i++) { #
    <tr>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H1', i) #" value="#= Scores[i].H1 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H2', i) #" value="#= Scores[i].H2 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H3', i) #" value="#= Scores[i].H3 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H4', i) #" value="#= Scores[i].H4 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H5', i) #" value="#= Scores[i].H5 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H6', i) #" value="#= Scores[i].H6 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H7', i) #" value="#= Scores[i].H7 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H8', i) #" value="#= Scores[i].H8 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H9', i) #" value="#= Scores[i].H9 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H10', i) #" value="#= Scores[i].H10 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H11', i) #" value="#= Scores[i].H11 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H12', i) #" value="#= Scores[i].H12 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H13', i) #" value="#= Scores[i].H13 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H14', i) #" value="#= Scores[i].H14 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H15', i) #" value="#= Scores[i].H15 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H16', i) #" value="#= Scores[i].H16 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H17', i) #" value="#= Scores[i].H17 #" /></td>
    <td><input data-bind="#= kendo.format('value: Scores[{0}].H18', i) #" value="#= Scores[i].H18 #" /></td>
    </tr>
    # } #
    </table>
    </script>

    @section Scripts
    {
    <script>
    $(function () {
    var
    score, template, placeholder,
    randomScore = function () { return Math.floor((Math.random()*10)+1); },
    model = { TeamName: 'John Doe', Scores: [] };

    for (var i = 0; i < 500; i++) {
    score = { Number: i };
    for (var j = 1; j <= 18; j++)
    score['H' + j] = randomScore();
    model.Scores.push(score);
    }

    model = kendo.observable(model);
    model
    .bind('get', function (e) {
    console.log('getting...' + e.field);
    })
    .bind('set', function (e) {
    console.log('setting...' + e.field);
    });

    template = kendo.template($('#template').html());
    placeholder = $('#placeholder');
    placeholder.html(template(model));
    kendo.bind(placeholder, model);

    $('#scorecard')
    .on('focus', 'input', function (e) {
    setTimeout(function () {
    $(e.target).select();
    })
    })
    .on('input', 'input', function (e) {
    var next = $(this).closest('td').next().find('input').focus();
    if (!next.length)
    $(this).closest('tr').next().find('input:first').focus();
    });

    });
    </script>
    }

    @section Styles
    {
    <style>
    #scorecard input { width: 2em; }
    </style>
    }

    ================================================================
  2. Petur Subev
    Admin
    Petur Subev avatar
    1882 posts

    Posted 28 Nov 2013 Link to this post

    Hello Grant,

    Indeed this is by design, when you bind the ViewModel to whatever is generated from the ListView the following workflow happens:

    1. You change an input
    2. The change event of the bound item is triggered
    3. It bubbles and reaches the dataSource of the listView
    4. The change event of the dataSource is triggered 
    5. The listview listens for this change event of the dataSource and it redraws its template

    Sadly I cannot offer you a work-around to this case.

    I am sorry for any inconvenience caused.

    If you have an idea how to improve the framework in that direction please share it on the feedback portal

    Kind Regards,
    Petur Subev
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  3. Kendo UI is VS 2017 Ready
  4. Grant Parker
    Grant Parker avatar
    2 posts
    Member since:
    Oct 2009

    Posted 02 Dec 2013 Link to this post

    Hey Petur,

    I don't know what "ListView" you are referring too.  I am not using one. 

    If you run my demo that I made especially for this post, you will see a table with 500 rows and 18 inputs in each row.  If you change the value of Score #1 in Row #1 all 9000 inputs are refreshed/rebound.  To verify this you can look at your browser's console you will see  9000 entries that look like this  "getting...Scores[0].H1", "getting...Scores[0].H2", all the way to "Scores[499].H18". 

    I have changed the demo to be a standalone "*.htm" file that uses your CDN, and attached it.  You should just be able to save to your desktop and click it.  Be sure to have your browser console open when you run it.

    ** Console Output **
       setting...Scores[0].H1
       getting...Scores[0].H1
       getting...Scores[0].H2
       getting...Scores[0].H3
       getting...Scores[0].H4
       getting...Scores[0].H5
       getting...Scores[0].H6
       getting...Scores[0].H7
       getting...Scores[0].H8
       getting...Scores[0].H9
       getting...Scores[0].H10
       getting...Scores[0].H11
       getting...Scores[0].H12
       getting...Scores[0].H13
       getting...Scores[0].H14
       getting...Scores[0].H15
       getting...Scores[0].H16
       getting...Scores[0].H17
       getting...Scores[0].H18
       getting...Scores[1].H1
       getting...Scores[1].H2
       getting...Scores[1].H3
       getting...Scores[1].H4
       getting...Scores[1].H5
       getting...Scores[1].H6
       getting...Scores[1].H7
       getting...Scores[1].H8
       getting...Scores[1].H9
       getting...Scores[1].H10
       getting...Scores[1].H11
       getting...Scores[1].H12
       getting...Scores[1].H13
       getting...Scores[1].H14
       getting...Scores[1].H15
       getting...Scores[1].H16
       getting...Scores[1].H17
       getting...Scores[1].H18
       getting...Scores[2].H1
       getting...Scores[2].H2
       getting...Scores[2].H3
       getting...Scores[2].H4
       getting...Scores[2].H5
       getting...Scores[2].H6
       getting...Scores[2].H7
       getting...Scores[2].H8
       getting...Scores[2].H9
       getting...Scores[2].H10
       getting...Scores[2].H11
       getting...Scores[2].H12
       getting...Scores[2].H13
       getting...Scores[2].H14
       getting...Scores[2].H15
       getting...Scores[2].H16
       getting...Scores[2].H17
       getting...Scores[2].H18
    ...
    ...
    ...
       getting...Scores[499].H18

  5. Petur Subev
    Admin
    Petur Subev avatar
    1882 posts

    Posted 04 Dec 2013 Link to this post

    Hello again,

    Thank you for sending the sample page. It seems I misunderstood your case - my apologies. I can suggest you to use source binding instead of the nested binding that you are trying.

    Check the attached example - you will avoid redrawing the whole hierarchy and also avoid focus issue.

    I hope this helps.

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title>Kendo MVVM Bug</title>
            <style>
                #scorecard input { width: 2em; }
            </style>
        </head>
        <body>
            <div id="placeholder"></div>
            <script type="text/x-kendo-template" id="template">
                <table id="scorecard">
                    <caption>#= TeamName #</caption>
                    <tbody data-bind="source:Scores" data-template="rowTemplate">
                    </tbody>
                </table>
            </script>
             
            <script type="text/kendo" id="rowTemplate">
                    <tr>
                        <td><input data-bind="value:H1" /></td>
                        <td><input data-bind="value:H2" /></td>
                        <td><input data-bind="value:H3" /></td>
                        <td><input data-bind="value:H4" /></td>
                        <td><input data-bind="value:H5" /></td>
                        <td><input data-bind="value:H6" /></td>
                        <td><input data-bind="value:H7" /></td>
                        <td><input data-bind="value:H8" /></td>
                        <td><input data-bind="value:H9" /></td>
                        <td><input data-bind="value:H10" /></td>
                        <td><input data-bind="value:H11" /></td>
                        <td><input data-bind="value:H12" /></td>
                        <td><input data-bind="value:H13" /></td>
                        <td><input data-bind="value:H14" /></td>
                        <td><input data-bind="value:H15" /></td>
                        <td><input data-bind="value:H16" /></td>
                        <td><input data-bind="value:H17" /></td>
                        <td><input data-bind="value:H18" /></td>
                    </tr>       
            </script>
     
     
            <script src="http://cdn.kendostatic.com/2013.2.918/js/jquery.min.js"></script>
            <script src="http://cdn.kendostatic.com/2013.2.918/js/kendo.all.min.js"></script>
            <script>
                $(function () {
                    var
                        score, template, placeholder,
                        randomScore = function () { return Math.floor((Math.random() * 10) + 1); },
                        model = { TeamName: 'John Doe', Scores: [] };
     
                    for (var i = 0; i < 500; i++) {
                        score = { Number: i };
                        for (var j = 1; j <= 18; j++)
                            score['H' + j] = randomScore();
                        model.Scores.push(score);
                    }
     
                    model = kendo.observable(model);
                    model
                        .bind('get', function (e) {
                            console.log('getting...' + e.field);
                        })
                        .bind('set', function (e) {
                            console.log('setting...' + e.field);
                        });
     
                    template = kendo.template($('#template').html());
                    placeholder = $('#placeholder');
                    placeholder.html(template(model));
                    kendo.bind(placeholder, model);
                });
            </script>
        </body>
    </html>


    Kind Regards,
    Petur Subev
    Telerik
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
Back to Top
Kendo UI is VS 2017 Ready