Add Data-Based Widgets to MVC Project

The DevExtreme UI Widgets Library includes widgets that are bound to data: dxDataGrid, dxGallery, dxMenu, dxLookup, etc. Data can be loaded from a locally located resource and from a remote service. To simplify working with data, DevExtreme supplies a Data Library. This library includes the DataSource object that keeps sorting, grouping, filtering and data transformation options, and applies them each time data is loaded. The DataSource underlying data access logic is isolated in a Store. Unlike the DataSource, a Store is a stateless object implementing a universal interface for reading and modifying data. Each Store contains the same set of methods. Several Store types are already implemented - Array Data Store (takes an array of data), Local Data Store (works with window.localStorage) and OData (works with OData protocol). All these stores have a single interface and thus can easily be changed to use another data source. Widgets from the Data Visualization library are also designed with an intention to support the DataSource-DataStore concept. In this tutorial, you will learn how to create a DataSource object for the dxDataGrid and dxChart widgets using a common Data Store. This will be a custom Store that accesses data from a custom service in a JSON format.

See Also
Download Code

Prepare a Sample MVC Application

  • Create a new MVC application project and add DevExtreme libraries as described in the Add Widget to MVC Project tutorial.

  • Add a Home controller and Index View. In addition, add a JavaScript file to implement JavaScript logic for the widgets that you will add to the Index view.

  • Add all required references to the Index view

    HTML
    <html>
    <head runat="server">
        <meta name="viewport" content="width=device-width" />
        <title>Index</title>
        <link rel="stylesheet" type="text/css" href="/Content/dx.common.css" />
        <link rel="stylesheet" type="text/css" href="/Content/dx.light.css" />
        <script src="/Scripts/jquery-2.1.4.min.js"></script>
        <script src="/Scripts/globalize/globalize.js"></script>
        <script src="/Scripts/dx.webappjs.debug.js"></script>
        <script src="/Scripts/dx.chartjs.debug.js"></script>
        <script src="/MyJS/script.js"></script>
    </head>
    <body>
    </body>
    </html>
  • Add a file with the following JSON object to the project.

    JavaScript
    [
        {
            "month": "january",
            "recordLow": -7,
            "recordHigh": 18,
            "average": 8.9,
            "color": "#00BFFF",
            "temperature": [
                { "day": 1, "t": 13.1 },
                { "day": 2, "t": 13.2 },
                //...
                { "day": 30, "t": 7.2 },
                { "day": 31, "t": 7.6 }
            ]
        },
        {
            "month": "february",
            //...
        }
    ]

    This object will serve as the data that came from "custom service". To simplify the tutorial and create an accent on how to transport received data to a widget, custom service will not be implemented.

Here is the resulting project structure.

Project Structure

See Also

Add a Grid

  • Open the Index view and add a div element that will contain the widget.

    HTML
    <div id="myGrid"></div>
  • Open the script.js file, which you added previously. Add the following code.

    JavaScript
    $(function () {
        // Creates a dxDataGrid widget using the appropriate jQuery plugin
        $("#grid").dxDataGrid();
    });
  • Create a CustomStore object to get JSON data from the file added in the previous step.

    JavaScript
    var customStore = new DevExpress.data.CustomStore({
        load: function (loadOptions) {
            var d = $.Deferred();
            $.getJSON('/Models/weatherData.js').done(function (data) {
                d.resolve(data, {totalCount: data.length});
            });
            return d.promise();
        }
    });

    The load method is required for getting data. You can make a more complex request for filtered or sorted data taking the load options passed as a parameter into account. In the code above, these options are not used to simplify the example for this given task.

    NOTE
    As you can see, a total count of items in the data source is specified. The grid will use this information to resolve paging and other features.
  • Define a configuration object for the DataSource object.

    JavaScript
    var gridDataSourceConfiguration = {
        store: customStore
    };

    Pass this configuration object to the grid's dataSource option. This configuration object will be used to create the DataSource object.

    JavaScript
    $(function () {
        $("#grid").dxDataGrid({
            dataSource: gridDataSourceConfiguration
        });
    });
    NOTE
    Do not create the Data Source object manually. It will be created by the grid internally. This is a dxDataGrid widget peculiarity. Other DevExtreme widgets can take on the manually created DataSource object. For details on grid data binding, refer to the Data Binding article.

    At this step, you can run the project and confirm that the grid is displayed with all loaded data.

  • Specify the columns that you need to be displayed in the grid.

    JavaScript
    $(function () {
        $("#grid").dxDataGrid({
            dataSource: gridDataSourceConfiguration,
            columns:[
                'month',
                'recordLow',
                'recordHigh'
            ]
        });
    });
  • Sort grid data locally.
    By default, sorting is enabled in the dxDataGrid widget. However, this feature will not work because the sorting options that are passed to the remote server to get sorted data are not processed. You can read details on this in the Data Binding | Provide Data | Using a CustomStore topic. In this tutorial, data sorting can be performed locally. For this purpose, set the sorting field of the remoteOperations configuration object to false.

    JavaScript
    $(function () {
        $("#grid").dxDataGrid({
            dataSource: gridDataSourceConfiguration,
            columns:[
                'month',
                'recordLow',
                'recordHigh'
            ],
            remoteOperations: {
                sorting: false
            }
        });
    });
See Also

View the result below.

Add a Chart

The data that comes as a JSON in this application allows you to display temperature flow during each month on a chart. For this purpose, add the dxChart widget.

HTML
<div id="chart1"></div>
JavaScript
$("#chart1").dxChart();

The Data Store that was created for the grid in the step below, loads an array of the following objects.

[
    {
        "month": "january",
        "recordLow": -7,
        "recordHigh": 18,
        "average": 8.9,
        "color": "#00BFFF",
        "temperature": [
            { "day": 1, "t": 13.1 },
            { "day": 2, "t": 13.2 },
            //...
            { "day": 30, "t": 7.2 },
            { "day": 31, "t": 7.6 }
        ]
    },
    {...},
    ...
]

This object structure is not appropriate for using the temperature | day field as a data source for chart arguments and the temperature | t field - for chart values. The following data structure is expected to be used for the chart.

[
    { "day": 1, "january": 13.1, "february": 6.4, ...},
    { "day": 2, "january": 13.1, "february": 6.4, ...},
    ...
]

or

[
    {"month": "january", "day": 1, "t": 13.1},
    {"month": "january", "day": 2, "t": 13.2},
    ...
]

The second variant is possible if you process the data received by the Store. To do this, use the postProcess configuration option for the DataSource object.

var chartDataSourceConfiguration = {
    store: customStore,
    postProcess: function(data) {
        var result = [];
        $.each(data, function () {
            var month = this.month;
            $.each(this.temperature, function () {
                this.month = month;
                result.push(this);
            });
        });
        return result;
    }
};

Create the DataSource object using chartDataSourceConfiguration as a configuration object.

Assign this DataSource object to the chart's dataSource option.

JavaScript
$("#chart1").dxChart({
    dataSource: new DevExpress.data.DataSource(chartDataSourceConfiguration)
});

Define a series template and specify common options for chart series.

JavaScript
$("#chart1").dxChart({
        dataSource: new DevExpress.data.DataSource(chartDataSourceConfiguration),
        seriesTemplate: {
            nameField: 'month',
        },
        commonSeriesSettings: {
            argumentField: 'day',
            valueField: 't',
            type: 'line',
            point: { visible: false }
        }
    });

Now you can see the chart in the browser. You can configure it using other configuration options to tune it up to the required view. For instance, specify the start and the end of the argument axis, so that non-existing days are not displayed on it.

JavaScript
$("#chart1").dxChart({
        dataSource: new DevExpress.data.DataSource(chartDataSourceConfiguration),
        seriesTemplate: {
            nameField: 'month',
        },
        commonSeriesSettings: {
            argumentField: 'day',
            valueField: 't',
            type: 'line',
            point: { visible: false }
        },
        argumentAxis: {
            min: 1,
            max: 31,
            minValueMargin: 0,
            maxValueMargin: 0
        },
        legend: {
            verticalAlignment: 'bottom',
            horizontalAlignment: 'center'
        }
    });
See Also

Here is the result.

Bind Chart to the Selected Grid Row

Now, add one more chart to display the temperature flow within the month that is currently selected in the grid.

HTML
<div id="chart2"></div>
JavaScript
$("#chart2").dxChart();

To provide data for this chart, enable selection in the grid and handle the selection change.

JavaScript
$("#chart2").dxChart({
    dataSource: [],
    series: {
        argumentField: 'day',
        valueField: 't',
        type: 'bar'
    },
    argumentAxis: {
        min: 1,
        max: 31,
        minValueMargin: 0.01,
        maxValueMargin: 0.01
    },
    legend: {
        visible: false
    }
});
$("#grid").dxDataGrid({
    dataSource: gridDataSourceConfiguration,
    columns:[
        'month',
        'recordLow',
        'recordHigh'
    ],
    remoteOperations: {
        sorting: false
    },
    onSelectionChanged: function(selectedItems){
        if (selectedItems.selectedRowKeys.length) {
            $("#chart2").dxChart("instance").option("dataSource", selectedItems.selectedRowsData[0].temperature);
        }
    }
});

The last thing to do is to specify a grid row to be selected initially. For this purpose, use the grid's selectedRowKeys option. To specify this option, there should be a key field in the provided data source. So, specify the key configuration option for the custom store that is used for the widgets.

JavaScript
var customStore = new DevExpress.data.CustomStore({
    key: 'month',
    //...
});
$("#grid").dxDataGrid({
    dataSource: gridDataSourceConfiguration,
    columns: [
        'month',
        'recordLow',
        'recordHigh'
    ],
    remoteOperations: {
        sorting: false
    },
    selection: {
        mode: 'single'
    },
    selectedRowKeys: ['January'],
    selectionChanged: function (selectedItems) {
        if (selectedItems.selectedRowKeys.length) {
            $("#chart2").dxChart("instance").option("dataSource", selectedItems.selectedRowsData[0].temperature);
        }
    }
});

Here is the result.