javascriptc#.net-corekendo-gridkendo-ui-mvc

Set datasource of child grid using datasource of parent grid in Kendo UI


I have this form:

@*Some form fields here that accept startDate and endDate*@

<div>
    <button id="searchButton">Search</button>
</div>
<div class="col-md-12 row">
    @(Html.Kendo()
            .Grid<ProjectName.DataModels.Models.Customer>()
            .Name("CustomerGrid")
            .Columns(columns =>
            {
                columns.Bound(e => e.CustomerId);
                columns.Bound(e => e.SomeCustomerColumn);
            })
            .ClientDetailTemplateId("OrderDetails")
            .AutoBind(false) // Don't load the data yet because I'll need to supply parameters for the fetch
            .DataSource(dataSource => dataSource
                        .Ajax()
                        .Events(events=>events.Change("loadChildGrid"))
                        .PageSize(20)
                        .Model(model => model.Id("CustomerId", typeof(string)))
                        .Read(read => read.Action("GetCustomersAsync", "Customer").Data("passArguments"))
            )
    )

    <script id="OrderDetails" type="text/kendo-tmpl">
        @(Html.Kendo()
                .Grid<ProjectName.DataModels.Models.Order>()
                .Name("OrderDetails_#=CustomerId#")
                .Columns(columns =>
                {
                    columns.Bound(o => o.ProductName);
                    columns.Bound(o => o.SomeOrderColumn);
                })
                .DataSource(dataSource => dataSource
                            .Ajax()
                            .PageSize(10)
                            .Model(model=>model.Id("OrderId"))
                            .ServerOperation(false)
                )
                .AutoBind(false)
                .ToClientTemplate()
        )
    </script>
</div>
 
<script type="text/javascript">
    $("#searchButton").on("click", function () {
        // Load the customerGrid here:
        $("#CustomerGrid").data("kendoGrid").dataSource.read();
    });
    
    function passArguments() {
        var startDate = $("#startdate").data("kendoDatePicker").value();
        var endDate = $("#enddate").data("kendoDatePicker").value();
        return {
            start: startDate,
            end: endDate
        }
    }
    
    // QUESTION: How to load the child grid: OrderDetails_123 by using datasource from the parent grid?
    // THIS IS WHAT I'VE TRIED SO FAR:
    function loadChildGrid() {
        var parentData = $("#CustomerGrid").data("kendoGrid").dataSource.data();
        //Initialize the  child grid
        $.each(parentData, childDataFeeder);
    }
    
    function childDataFeeder(index, item) {
        var childGridName = "#" + "OrderDetails_" + item.CustomerId;
        var childGrid = childGridName.data("kendoGrid");
        childGrid.dataSource.data(value.Orders)
    }
</script>

And a method in the Customer controller:

public async Task<ActionResult> GetCustomersAsync([DataSourceRequest] DataSourceRequest request, DateTime start, DateTime end)
{
    var customersWithOrders = GetDataForParentAndChildGrid(start, end);
    return Json(customersWithOrders.ToDataSourceResult(request));
}

private List<Customer> GetDataForParentAndChildGrid(DateTime start, DateTime end)
{
    var testData = new List<Customer>();
    // Gets required data with those dates filter and perform some mathematical calculations
    testData.Add(new Customer
    {
        CustomerId = "123",
        SomeCustomerColumn = "Blah blah",
        Orders = new List<Order>()
        {
            new Order{
                OrderId = "123ABC",
                CustomerId = "123",
                SomeOrderColumn = "Blah Blah Blah"
            }
        }
    });
    return testData;
}

My goal is to set the dataSource of child grid using data that is already available from the main grid. What I've tried so far is that I have attached Change event to the main grid which fires loadChildGrid function where I try to extract the data from main grid and pass every item of it to a childDataFeeder function to initialize the dataSource of child grid. The issue here is that when it tries to do that, the child grid doesn't exist yet (because it's not created by Kendo until a user clicks on the expand icon in the main grid).

You can see what I've tried so far in the childDataFeeder method(without any success). So I'd greatly appreciate your direction on this.

Thank You!


Solution

  • After so many hours of hit and trial, I finally solved it. So I'm posting it here to save someone else's time if they come across a similar problem:

    I added a DetailExpand event to the main grid. And removed the Change event on the dataSource.

    @(Html.Kendo()
            .Grid<ProjectName.DataModels.Models.Customer>()
            .Name("CustomerGrid")
            .Columns(columns =>
            {
                columns.Bound(e => e.CustomerId);
                columns.Bound(e => e.SomeCustomerColumn);
            })
            .ClientDetailTemplateId("OrderDetails")
            .AutoBind(false) // Don't load the data yet because I'll need to supply parameters for the fetch
            .DataSource(dataSource => dataSource
                        .Ajax()
                        .PageSize(20)
                        .Model(model => model.Id("CustomerId", typeof(string)))
                        .Read(read => read.Action("GetCustomersAsync", "Customer").Data("passArguments"))
            )
            .Events(events => events.DataBound("dataBound").DetailExpand("onExpand"))
    )
    

    Now the callback function called onExpand will be called every time we expand a row in the parent grid. This is where I will now set the child grid's dataSource.

    // Passing e is also important here because if you don't, this callback gets called 
    // for every row in the main grid (even when you don't expand them!)
    function onExpand(e) {
        var customerId = e.sender.dataItem(e.masterRow).CustomerId;
        var orders = e.sender.dataItem(e.masterRow).Orders;
        //Initialize the  child grid as well
        var childGridName = "#" + "OrderDetails_" + customerId;
    
        var childGrid = $(childGridName).data("kendoGrid");
        if (childGrid !== undefined) {
            childGrid.dataSource.data(orders);
        }
    }
    
    function dataBound() {
        this.expandRow(this.tbody.find("tr.k-master-row").first());
    }
    

    I used e.sender.dataItem(e.masterRow).PROPERTYNAME to access the properties that I need from the master row.

    This works flawlessly now!