New to Telerik UI for ASP.NET MVCStart a free 30-day trial

Drilldown Charts

The Telerik UI for ASP.NET MVC Chart supports a drill-down functionality that allows the user to explore the data.

The drill-down function allows users to click on a point (bar, pie segment, etc.) in order to navigate to a different view. The new view usually contains finer-grained data about the selected item, like breakdown by product of the selected category.

The view hierarchy is displayed in a breadcrumb for easy navigation back to previous views.

Getting Started

To configure a chart series for drill-down:

  1. Set the DrilldownField() to a field that contains the drill-down series configuration for each point.
  2. Add a ChartBreadcrumb component and link it to the Chart.
Razor
    @(Html.Kendo().ChartBreadcrumb()
        .Name("cb")
        .Chart("chart")
    )

    @(Html.Kendo().Chart<Kendo.Mvc.Examples.Models.CompanyModel>()
        .Name("chart")
        .Series(series =>
        {
            series.Column(model => model.Sales)
            .Name("Company sales")
            .CategoryField("CompanyName")
            .DrilldownField("Products")
            .DrilldownSeriesFactory("drillDownHandler");
        })
        .DataSource(dataSource => dataSource.Read(read => read.Action("Get_Companies", "Drilldown_Charts")))
        .Legend(legend => legend.Position(ChartLegendPosition.Bottom))
    )

    <script>
        function drillDownHandler(chartPoint) {
            return {
                type: 'column',
                name: chartPoint.parent().CompanyName + ' Products',
                data: chartPoint,
                field: 'Sales',
                categoryField: 'ProductName',
            };
        }
    </script>

Drilling Down with Dynamic Data

The drill-down functionality enables you to drill past arbitrary data which is based on a upward level criteria.

To populate the drill-down series on dynamically:

  1. Set the DrilldownField() option to a field that contains the drill-down value field for each point.
  2. Define a DrilldownSeriesFactory() function handler that returns the series definition for each data point.
Razor
    @(Html.Kendo().ChartBreadcrumb()
         .Name("cb")
         .Chart("chart")
    )

    @(Html.Kendo().Chart<Kendo.Mvc.Examples.Models.VehicleMake>()
        .Name("chart")
        .Series(series => 
        {
           series.Column(model => model.Count)
           .Name("Battery EVs registered in 2022")
           .CategoryField("Company")
           .DrilldownField("Company")
           .DrilldownSeriesFactory("drilldownByModel");
        })
        .DataSource(dataSource => dataSource.Read(read => read.Action("Get_VehicleMakes","Drilldown_Charts")))
        .Legend(legend => legend.Position(ChartLegendPosition.Bottom))
    )

    <script>
       var vehiclesByQuarter = @Html.Raw(Json.Encode(@ViewData["VehiclesByQuarter"]));
       var vehiclesByModel = @Html.Raw(Json.Encode(@ViewData["VehiclesByModel"]));
       function drilldownByQuarter(model) {
           const data = vehiclesByQuarter[model];
           
           if (data) {
               return {
                   type: 'column',
                   name: model + ' Sales by Quarter',
                   data,
                   field: 'Count',
                   categoryField: 'Period'
               };
           }
       }
       function drilldownByModel(make) {
           const data = vehiclesByModel[make];
           if (data) {
               return {
                   type: 'column',
                   name: make + ' Sales by Model',
                   data,
                   field: 'Count',
                   categoryField: 'Model',
                   drilldownField: 'Model',
                   drilldownSeriesFactory: drilldownByQuarter
               };
           }
       }
    </script>     

Drilling Down with Async Data

The drill-down functionality gives you the ability to configure the drill levels in an asynchronous manner by using a Promise.

To populate the drilldown series asynchronously:

  1. Set the DrilldownField() option to a field that contains the drill-down value field for each point.
  2. Define a DrilldownSeriesFactory() function handler that returns a Promise that resolves to the series definition for each data point.
Razor
    @(Html.Kendo().ChartBreadcrumb()
         .Name("cb")
         .Chart("chart")
    )

    @(Html.Kendo().Chart<Kendo.Mvc.Examples.Models.VehicleMake>()
        .Name("chart")
        .Series(series => 
        {
           series.Column(model => model.Count)
           .Name("Battery EVs registered in 2022")
           .CategoryField("Company")
           .DrilldownField("Company").DrilldownSeriesFactory("drilldownByModel");
        })
        .DataSource(dataSource => dataSource.Read(read => read.Action("Get_VehicleMakes","Drilldown_Charts")))
        .Legend(legend => legend.Position(ChartLegendPosition.Bottom))
    )

    <script>
       var vehiclesByModel = @Html.Raw(Json.Encode(@ViewData["VehiclesByModel"]));
       
       function drilldownByModel(make) {
         return new Promise(function(resolve, reject) {
            const data = vehiclesByModel[make];
            if (data) {
                resolve({
                    type: 'column',
                    name: make + ' Sales by Model',
                    data,
                    field: 'count',
                    categoryField: 'model'
                });
            } else {
                reject('No data for ' + model);
            }
          });
       }
    </script>     

Customizing the Breadcrumb Root Item

To customize the root item of the Chart's Breadcrumb and change its appearance, set the RootItem() of the ChartBreadCrumb component.

Razor
    @(Html.Kendo().ChartBreadcrumb()
        .Name("cb")
        .RootItem(rootItem => {
            rootItem.Type("rootItem");
            rootItem.Text("Home");
            rootItem.ShowIcon(false);
            rootItem.ShowText(true);
        })
        .Chart("chart")
    )

    @(Html.Kendo().Chart<Kendo.Mvc.Examples.Models.CompanyModel>()
        .Name("chart")
        .Series(series =>
        {
            series.Column(model => model.Sales)
            .Name("Company sales")
            .CategoryField("CompanyName")
            .DrilldownField("Products")
            .DrilldownSeriesFactory("drillDownHandler");
        })
        .DataSource(dataSource => dataSource.Read(read => read.Action("Get_Companies", "Drilldown_Charts")))
        .Legend(legend => legend.Position(ChartLegendPosition.Bottom))
    )

    <script>
        function drillDownHandler(chartPoint) {
            return {
                type: 'column',
                name: chartPoint.parent().CompanyName + ' Products',
                data: chartPoint,
                field: 'Sales',
                categoryField: 'ProductName',
            };
        }
    </script>

Implementing Custom Navigation

The drill-down functionality enables you to alter the default navigation and provide a custom incarnation of your own, by programmatically changing the navigational items.

To implement a custom drill-down navigation:

  1. Handle the DrillDown event to append new drill-down levels to the navigation.
  2. Within the handler, call the resetDrilldownLevel() client-side method to return to a previous level.
Razor
    @(Html.Kendo().ChartBreadcrumb()
        .Name("cb")
        .Events(events => events.Click("onBreadcrumbClick"))
        .Chart("chart")
    )

    @(Html.Kendo().Chart<Kendo.Mvc.Examples.Models.CompanyModel>()
        .Name("chart")
        .Events(events => events.Drilldown("onDrillDown"))
        .Series(series =>
        {
            series.Column(model => model.Sales)
            .Name("Company sales")
            .CategoryField("CompanyName")
            .DrilldownField("Products").DrilldownSeriesFactory("drillDownHandler");
        })
        .DataSource(dataSource => dataSource.Read(read => read.Action("Get_Companies", "Drilldown_Charts")))
        .Legend(legend => legend.Position(ChartLegendPosition.Bottom))
    )

    <script>
        var navItems = [{
          type: 'rootitem',
          icon: 'home',
          text: 'Home',
          showIcon: true
        }];

        function refreshBreadcrumb() {
          var breadcrumb = $('#cb').getKendoBreadcrumb();
          breadcrumb.items(navItems);
        }

        function onDrilldown(e) {
            navItems.push({
              text: e.point.category.toString()
            });
            refreshBreadcrumb();
        }

        function onBreadcrumbClick(e) {
            var level = navItems.indexOf(e.item);
            $("#chart").getKendoChart().resetDrilldownLevel(level);
            navItems = navItems.slice(0, level + 1);
            refreshBreadcrumb();
        }

        function drillDownHandler(chartPoint) {
            return {
                type: 'column',
                name: chartPoint.parent().CompanyName + ' Products',
                data: chartPoint,
                field: 'Sales',
                categoryField: 'ProductName',
            };
        }
    </script>

See Also