Add to array property of grid dataSource

6 posts, 0 answers
  1. Jark Monster
    Jark Monster avatar
    46 posts
    Member since:
    Jan 2012

    Posted 10 Apr 2013 Link to this post

    Build: 2012.3.1413

    When my batch grid is saved, I am trying to add items to a list property that is present on the grid's dataSource.  I am getting an accurate quantity of items inserted into this list property, but I am not getting any of the data that should be present with it.  The strange thing is that the values DO appear to be present when I use firebug to look at what is being sent to the server, but the data disappears on the object on the controller side (except for the appropriate number of entries)

    ItemModel
    public class ItemModel
     {
         public ItemModel()
         {
             ServiceBookList = new List<ServiceModel>();
         }
     
         public List<ServiceModel> ServiceBookList { get; set; }
    }
    ServiceModel
    public class ServiceModel
    {
        public ServiceModel() { }
     
        public int RefServiceID { get; set; }
        public string Value { get; set; }
        public string Code { get; set; }
     
        public long EnterpriseProductServiceBookID { get; set; }
        public long AppUserProductServiceBookID { get; set; }
        public bool IsDeleted { get; set; }
    }
    Script
    function SaveProductBook() {
           //This is called when the "Save" button on the parent grid is clicked
         var productBook = $("#AdminProductBookGrid").data("kendoGrid");
         SaveProductDetailViews(productBook); //Save items located in detail template
         productBook.dataSource.sync();
     }
       
    function SaveProductDetailViews(itemGrid) {
         //For each row in grid, grab the latest data from the fields within the detail templates
         //and update the row's dataSource prior to sync
         $.each($("#AdminProductBookGrid tbody tr"), function (index, row) {
             var dataItem = itemGrid.dataItem(row);
             var detailRow = $(row).next();
     
             if (dataItem != null && detailRow.has('.k-detail-cell').length > 0) {
                 SaveServiceDetailViews(dataItem, detailRow, type);
             }
         });
     }
     
    function SaveServiceDetailViews(dataItem, detailRow, type) {
         var serviceGrid = $(detailRow).find("div[id=ServiceGrid_" + dataItem.uid + "]").data("kendoGrid");
        //data is the item's added in the detail template's grid for that parent row
        var data = serviceGrid.dataSource._data;
         for(var i = 0; i < data.length; i++) {      
               //Add to the parent row's dataSource list
               dataItem.ServiceBookList.push(data[i]);
         }
     }
    DataSource state prior to grid sync() - From Firebug
    See image below, but the 1 attribute that should be populated is RefServiceID, and it is. 

    Result on POST -from Firebug

    See image below, but same as DataSource (ie. RefServiceID should be the only attribute populate in the sub list items and it is.
    ***Issue is possible here because the full path of the data in the list is written strangely, but I'm not sure if it is correct.
    Full path to RefServiceID (after push function) is:
    models[0].ServiceBookList[0][RefServiceID]
    I figure it should look list other arrays and be like this (but not sure):
    models[0].ServiceBookList[0].RefServiceID

    Controller values
    See Image Below


    In case my description was insufficient, I have also included a screenshot of how everything is laid out in the application.

    Please let me know if further code is needed.  Also, I began this when there was no out of the box support for saving editable fields in a grid's detail template.  Please let me know if this has changed and how I might go about doing it in a supported way.

    Thanks!
  2. Daniel
    Admin
    Daniel avatar
    2219 posts

    Posted 15 Apr 2013 Link to this post

    Hello,

    The dataSource will not automatically serialize the arrays in a format that can be processed by the model binder. You should either add the fields to the dataItem in the needed format or use the request data function to serialize the array e.g.

    .Update(update=> update.Action("action", "controller").Data("serialize"))
    function serialize(options) {       
        var data = options.models,
            bookList;
        for (var i = 0; i < data.length; i++) {
            bookList = data[i].ServiceBookList;
            for (var j = 0; j < bookList.length; j++) {
                for (var field in bookList[i]) {
                    data[i]["ServiceBookList[" + j + "]." + field] = bookList[j][field];
                }               
            }
            delete data[i].ServiceBookList;
        }       
    }

    I am not sure if I understand correctly this question:
    there was no out of the box support for saving editable fields in a grid's detail template The detail Grid can be configured to be editable and save the changes to the server as in any other case. If you need to save the detail Grid data with the master Grid request then updating the parent model will be needed.
    Regards,
    Daniel
    the Telerik team
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  3. Jark Monster
    Jark Monster avatar
    46 posts
    Member since:
    Jan 2012

    Posted 16 Apr 2013 Link to this post

    I have verified that this does indeed work.  Thank you!

    To clarify my earlier remark, I was more specifically told that an editable form was not natively supported in that I couldn't rely on the "Save Changes" button to save the form as well.

    I have encountered another issue however that is loosely related.

    When the Save button is clicked, I loop through all of the grid rows and see if the detail template exists.  If it does, I loop through the data in the detail template grid.  When editing one row, this works perfectly.  However, my ability to detect previously open detail templates (ie. The html has been added to the view as a <tr>) is compromised when I open multiple detail templates.  I have narrowed it down to the read function on the detail template grid, and I believe when it posts the json result back, I am 'breaking' jquery's ability to reference other row detail templates.

    Here is the parent grid detail template code:
    <script id="EnterpriseProductDetailsAdmin" type="text/kendo-tmpl">
        @(Html.Kendo().TabStrip()
            .Name("Details_#=uid#") //Name is important to 'detect' detail template existence
            .SelectedIndex(0)
            .Items(items =>
            {
               items.Add().Text("Services")
                   .Content
                        (@<div id="ItemGridDetail_ServiceContainer">
                         @Html.Partial("Admin/AdminServices")</div>);
            })
            .ToClientTemplate()
        )
    </script>

    Here is my code from the grid in the detail template:
    @(Html.Kendo().Grid<BlueGrace.BlueShip.MVC.Models.ServiceModel>()
        .Name("ServiceGrid_#=uid#")
        .Columns(columns =>
        {
            columns.ForeignKey(item => item.RefServiceID,
                    (System.Collections.IEnumerable)ViewBag.RefServiceListing, "RefServiceID", "Value");
            columns.Bound(item => item.Code).Hidden(true);
            columns.Command(command =>
            {
                command.Destroy();
            });
        })
        .ToolBar(toolbar =>
        {
            toolbar.Create();
        })
        .Editable(editable =>
        {
            editable.Mode(GridEditMode.InCell).CreateAt(GridInsertRowPosition.Top);
            editable.DisplayDeleteConfirmation(false);
        })
        .Sortable()
        .Pageable()
        .Scrollable()
        .DataSource(dataSource => dataSource
            .Ajax()
            .Batch(true)
            .ServerOperation(false)
            .Model(model =>
            {
                model.Id(i => i.ID);
            })
            .Read(read => read.Action("GetServices", "Home", new { ID = "#=ID#" }))
        )
        .Pageable()
        .ToClientTemplate()
    )
    Here is the code I use to 'detect' the detail template:
    function SaveProductBook() {
        var productBook = $("#ProductGrid").data("kendoGrid");
        SaveProductDetailViews(productBook);
        //productBook.dataSource.sync();  //Will call this to save
    }
     
    function SaveProductDetailViews(productBook) {
        for(var i = 0; i < productBook.dataSource._data.length; i++) {
            var dataItem = productBook.dataSource._data[i];
            var detailTemplate = $("#Details_" + dataItem.uid);
                       //NOTE: This is the DIV surround the detail template's tabstrip
            console.log("dataItem");
            console.log(dataItem);
            console.log(dataItem.uid);
            console.log("template");
            console.log(detailTemplate);
            if(detailTemplate.length > 0) {
                //if it exists, then proceed with serializing service grid to parent dataItem
                SaveServiceDetailViews(dataItem, detailTemplate);
            }
        }
    }
    When I have a parent grid with 2 rows in it, and after it loads, I open both detail templates and click "Save Changes" on the parent grid, I get this result from my console.log() functions and only the first row's detail template data is serialized:  SEE ATTACHED IMAGE

    IF I comment out the read method on the detail template grid, this issue goes away, but obviously, I don't 'get' my data, so there's definitely an issue still.  Thanks for the help so far, and I hope this is a small issue.
  4. Daniel
    Admin
    Daniel avatar
    2219 posts

    Posted 18 Apr 2013 Link to this post

    Hello,

    Sorry, there is a mistake in the code from my previous reply. Please try with the following code instead:

    function serialize(options) {      
        var data = options.models,
            bookList;
        for (var i = 0; i < data.length; i++) {
            bookList = data[i].ServiceBookList;
            for (var j = 0; j < bookList.length; j++) {
                for (var field in bookList[j]) {
                    data[i]["ServiceBookList[" + j + "]." + field] = bookList[j][field];
                }              
            }
            delete data[i].ServiceBookList;
        }      
    }
    Regards,
    Daniel
    the Telerik team
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  5. Jark Monster
    Jark Monster avatar
    46 posts
    Member since:
    Jan 2012

    Posted 18 Apr 2013 Link to this post

    Thank you for the correction.

    I have gotten this working except for one issue:  When I edit the child grid in 2 or more rows, I lose the 'connection' to all but the last detail template opened.

    I believe that the read method's POST might be the culprit.  I feel this is so for a couple of reasons:
    1. I can reference the elements within the detail template of the last row that was expanded.***
    2. When I comment out the read method, though I get no data in my subgrid, I can reference the elements within each opened detail template
    ***NOTE: I found this by trying to reference the elements of the subgrid using jQuery while trying to serialize the detail template's onto the parent grid's dataItem during my overridden save function.

    Here is the code where I'm unable to detect multiple open detail templates:
    for(var i = 0; i < productBook.dataSource._data.length; i++) {
        var dataItem = productBook.dataSource._data[i];
        var detailTemplate = $("#Details_" + dataItem.uid);
             //This comes back empty on all rows except the LAST
             //row that had its detail template opened
     
        if(detailTemplate.length > 0) {
            //This code is only reached by the 'last' detail template opened
            SaveServiceDetailViews(dataItem, detailTemplate);
        }
    }
    Is there a better way to detect if a given row has its detail template expanded?
  6. Daniel
    Admin
    Daniel avatar
    2219 posts

    Posted 22 Apr 2013 Link to this post

    Hi,

    ServiceBookList will be observable array so using the push method will trigger the change event and the master grid will be redrawn. You could initialize a JavaScript array in order to avoid triggering the event:

    function SaveServiceDetailViews(dataItem, detailRow, type) {
        var serviceGrid = $(detailRow).find("div[id=ServiceGrid_" + dataItem.uid + "]").data("kendoGrid");
        var data = serviceGrid.dataSource._data;
        data.ServiceBookList = [];
        for(var i = 0; i < data.length; i++) {     
              //Add to the parent row's dataSource list
              dataItem.ServiceBookList.push(data[i]);
        }
     }
    Regards,
    Daniel
    the Telerik team
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
Back to Top