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

ObservableObject with dataSource used in Grid

1 Answer 341 Views
MVVM
This is a migrated thread and some comments may be shown as answers.
Jacques
Top achievements
Rank 2
Jacques asked on 27 Nov 2013, 03:41 PM
We're in the process of changing one of our complex grids to make use of MVVM the purpose of which was to be able to perform calculations when certain events occur. 

The grid shows a list of financial asset classes, investment products, weights and returns. The complexity of the grid comes in where the dataSource consists of a number of records as well as a nested array representing investment product choices along with their returns. E.g. 

JSON: 
[{ "AssetClassCode": "XYZ", "AssetClassName": "Domestic Bonds", "Benchmarks": [{"ExtensionData":{},"AssetClassCode":"XYZ", "InvestmentCode": "ABC", "YTD": 0.14, "Year1": 0.23, "Year3": 0.21, "Year5": 0.23}]}]
This represents one asset class with one or more investment products. The payload also carries performance data with each investment product. 

This is represented in the Grid as a row in which the Investment Product column is a drop down list which the user can use to choose a different investment product. Each time an investment product is selected the returns columns (Ytd, Year1, Year3, Year5) are updated with the data from the JSON data above. 

In the observable object we have a function which we use to bind each row's Investment Products drop down list's change event to. See the following code. 
viewModel = kendo.observable({
 
                assetClassesDataSource: dataSource,
 
                save: function ()
                {
                    console.log("saving");
                },
 
                edit: function ()
                {
                    console.log("edit");
                },
 
                onBenchmarkChange: function (args)
                {
                    args.data.set("InvestmentCode ", args.sender.dataItem().InvestmentCode );
                    args.data.set("Allocation", 0);
                    args.data.set("Ytd", args.sender.dataItem().Ytd);
                    args.data.set("Year1", args.sender.dataItem().Year1);
                    args.data.set("Year3", args.sender.dataItem().Year3);
                    args.data.set("Year5", args.sender.dataItem().Year5);
 
                    console.log("done setting");
                }
 
...
The problems here are: 
1. You can't set multiple values at once. Confirmed by a Telerik response on one of the forums. This is not a problem in itself if it weren't for fact that each .set call fires internal events resulting in a VERY slow update of the grid. 
2. Using the following code, the save and edit events are never fired from the Kendo grid

<div data-role="grid" data-editable="true" data-bind="source: assetClassesDataSource, events: { save: save, edit: edit, dataBound: dataBound }"
            data-columns="[ {'field': 'AssetClassName', 'width': '25%', 'title': 'Asset Class'},
            {'field': 'InvestmentCode', 'width': 190, 'title': 'Investment Code'},
            {'field': 'InvestmentName', 'width': '25%', 'title': 'Investment'},
            {'field': 'Allocation', 'width': 75, headerAttributes: { 'class': 'text-right'}, groupFooterTemplate: '#: sum #%', footerTemplate: '# if(data.Allocation) { if (sum > 100) {# <span class=\'message-error\'>#= sum #%</span> #} else {# <span class=\'\'>#= sum #%</span> #} } else {# 0 #}#'},
            {'field': 'Ytd', 'width': 75, 'title': 'YTD', headerAttributes: { 'class': 'text-right'}},
            {'field': 'Year1', 'width': 75, 'title': '1 Year', headerAttributes: { 'class': 'text-right'}},
            {'field': 'Year3', 'width': 75, 'title': '3 Year', headerAttributes: { 'class': 'text-right'}},
            {'field': 'Year5', 'width': 75, 'title': '5 Year', headerAttributes: { 'class': 'text-right'}}]"
            data-row-template="row-template">
        </div>
 
<script id="row-template" type="text/x-kendo-template">
        <tr>
            <td></td>
            <td data-bind="text: AssetClassName">
            </td>
            <td data-bind="text: BenchmarkCode">
            </td>
            <td>
                <input data-role="dropdownlist" data-text-field="InvestmentName" data-value-field="InvestmentCode" data-bind="{source: Benchmarks, events: {change: onBenchmarkChange, dataBound: onBenchmarksDataBound}}" data-value-primitive="true" />
            </td>
            <td class="text-right">
                <input class="editable" data-role="numerictextbox" data-format="N0" data-bind="{value: Allocation}" data-min="0" data-max="100" />
            </td>
            <td class="text-right">
                #: kendo.toString(get("Ytd"), "P") #
            </td>
            <td class="text-right">
                #: kendo.toString(get("Year1"), "P") #
            </td>
            <td class="text-right">
                #: kendo.toString(get("Year3"), "P") #
            </td>
            <td class="text-right">
                #: kendo.toString(get("Year5"), "P") #
            </td>
        </tr>
        </script>
3. The fact that we're not getting any events firing for the grid means that we can't calculate the sumProduct or weighted average values at the bottom of the grid. 
4. When you use this declarative approach to setting up the grid, the columns configuration is not the best way to do things. As you can see we have templates with JS if statements in them which are intended to output HTML into the Allocation Total row. If the value is higher than 100 the HTML rendered includes a class making the text appear red. The span's class attribute requires inverted commas and this was quite painful to figure, but the end result is that it works, but you can no longer use Visual Studio to correctly format the HTML document/view. 

We were contemplating writing custom aggregate functions for this purpose, but another forum post from Telerik confirmed that this is not supported. Our only choice then is to track a save/update type event on the grid and then manually update the dataSource aggregate values, or directly update the HTML total row to reflect our results. (I doubt the datasource will allow us to store calculated values in some sort of aggregates cache/variable). 

Although visually advanced, it just seems like kendo Grid is quite rudimentary when it comes to these slightly more complex types of requirements. ?? Perhaps that is just it, it was designed for simple, visually appealing grids with the ability to show master/detail, filterable, sortable, editable data. Getting anything more advanced to work like having a drop down list populated from the same nested JSON data that the dataSource uses pushes the limits. So much so, that in previous posts Telerik staff mentioned that this scenario was not possible, yet we got it working. 

So apart from points/questions 1-4 above a big question is whether or not we ditch kendo grid altogether in this scenario? 

1 Answer, 1 is accepted

Sort by
0
Petur Subev
Telerik team
answered on 29 Nov 2013, 01:04 PM
Hello Jacques,

Here is some additional info for the points that you noted

  1. You can set the values directly to the model if you do not want the Grid to refresh. e.g.
    var someDataItem = grid.dataItem(someTr);
    grid.refresh();
    As you noticed in order for the Grid to display what was changed, you need to invoke the refresh method
  2. Save events, edit events are triggered when the user changes the records via interaction, they wont be triggered if you change any of the methods programatically. To get notified for such changes consider using the change event of the dataSource.
  3. Same as 2
  4. Aggregates will be re-calculated if the set methods of the models are used. If after editing a record you want the aggreagates to be re-caulcualted automatically (and you are not using serverAggregates). Then you just need to invoke the refresh method of the Grid when the save event of the Grid is triggered (keep in mind this is redraw the whole grid).



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!
Tags
MVVM
Asked by
Jacques
Top achievements
Rank 2
Answers by
Petur Subev
Telerik team
Share this question
or