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

Three, Four or more level of hierarchy on a grid

33 Answers 630 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Mithun
Top achievements
Rank 1
Mithun asked on 22 Feb 2013, 05:51 PM
Hi,

This demonstration very nicely shows how to implement two level hierarchy on a kendo grid. http://demos.kendoui.com/web/grid/hierarchy.html

I tried using the same coding structure to implement three or four levels of hierarchy but couldn't get it to work properly. It works, but the third level grid overwrites and overlaps the second level grid. Please could you let me know if three, four or more levels of hierarchy is possible on kendo web grid and if yes, I'll be grateful if you could point me to an example.

Many Thanks
MB

33 Answers, 1 is accepted

Sort by
0
Vladimir Iliev
Telerik team
answered on 26 Feb 2013, 10:12 AM
Hi Mithun,

 
It's possible to implement more than two nested grids using the same approach from the online demo, however currently there is no such demo which we can provide. Also from the provided information it's not clear for us what is the exact reason for overwriting/overlapping the parent grid - could you please provide your current grid code and more details how to reproduce the issue on our side?

Kind Regards,
Vladimir Iliev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Mithun
Top achievements
Rank 1
answered on 03 Mar 2013, 10:58 AM
Hi Vladimir ,

Thanks for getting back to me. Yes I did realize at a later stage that n level nested grids is indeed possible. What was happening in my case is the third level grid was ending up with same name as the second level grid; therefore, it was overlapping. I changed the dynamic naming logic on all levels and made sure that they are always unique and it worked like a charm after that!

Many thanks for offering help.

Much Appreciated,
Mithun
0
Stephen
Top achievements
Rank 1
answered on 30 May 2013, 04:16 PM
Hello,

I am having a problem making this happen.  I would like to have a grid with a tabstrip in it's detailtemplate.  That would contain another grid.  That grid would have a tabstrip in its detailtemplate which would in turn contain another grid.

GRID 1
    TabStrip
    -GRID 2
        TabStrip
        -GRID 3

I am using razor with server binding and I receive the following error:

Warning 1 Inline markup blocks (@<p>Content</p>) cannot be nested.  Only one level of inline markup is allowed.

Here is my code:
@{ Html.Kendo().Grid(Model)
    .Name("gvLaboratories")
    .Columns(columns =>
    {
        columns.Bound(l => l.ID);
        columns.Bound(l => l.Description);
    })
    .DetailTemplate(l =>
    {
        Html.Kendo().TabStrip()
            .Name("ts" + l.ID)
            .SelectedIndex(0)
            .Items(items =>
            {
                items.Add().Text("User Facilities").Content(@<text>
                                 
                    @(Html.Kendo().Grid(l.User_Facilities)
                        .Name("gvUserFacilities" + l.ID)
                        .Columns(columns =>
                        {
                            columns.Command(command => { command.Edit(); }).Width(50);
                            columns.Bound(f => f.ID);
                            columns.Bound(f => f.Description);
                            columns.Command(command => { command.Destroy(); }).Width(50);
                        })
                        .DetailTemplate(f =>
                        {
                            Html.Kendo().TabStrip()
                                .Name("ts" + f.ID)
                                .SelectedIndex(0)
                                .Items(childitems =>
                                {
                                    childitems.Add().Text("User Facility Admins").Content(@<text>       
 
                                        @(Html.Kendo().Grid(f.Users_Roles)
                                            .Name("gvUserFacilityAdmins" + f.ID)
                                            .Columns(columns =>
                                            {
                                                columns.Bound(a => a.User_ID).Template(a => a.User_ID.ToString()).Title("User ID");
                                                columns.Bound(a => a.User.BNL_ID).Title("BNL ID");
                                                columns.Bound(a => a.User.Pool.First_Name).Title("First Name");
                                                columns.Bound(a => a.User.Pool.Last_Name).Title("Last Name");
                                                columns.Bound(a => a.User.Account).Title("BNL Account");
                                                columns.Command(command => { command.Destroy(); }).Width(50);
                                            })
                                            .ToolBar(toolbar => toolbar.Create())
                                            .Editable(editable => editable.Mode(GridEditMode.PopUp))
                                            .Sortable()
                                            .DataSource(dataSource => dataSource
                                                .Server()
                                                .Model(model => model.Id(a => a.User_ID))
                                                .Read(read => read.Action("UserFacilities", "LabAdmin", new { facilityID = f.ID }))
                                                .Create(create => create.Action("AddUserFacilityAdmin", "LabAdmin"))
                                                .Destroy(destroy => destroy.Action("DeleteUserFacilityAdmin", "LabAdmin"))
                                            )
                                        )
 
                                    </text>);
                                })
                                .Render();
                        })
                        .ToolBar(toolbar => toolbar.Create())
                        .Editable(editable => editable.Mode(GridEditMode.PopUp))
                        .Sortable()
                        .DataSource(dataSource => dataSource
                            .Server()
                            .Model(model =>
                            {
                                model.Id(f => f.ID);
                                model.Field(field => field.ID).DefaultValue("");
                                model.Field(field => field.Description).DefaultValue("");
                            })
                            .Create(create => create.Action("AddUserFacility", "LabAdmin"))
                            .Read(read => read.Action("UserFacilities", "LabAdmin", new { labID = l.ID }))
                            .Update(update => update.Action("UpdateUserFacility", "LabAdmin"))
                            .Destroy(destroy => destroy.Action("DeleteUserFacility", "LabAdmin"))
                        )
                        .RowAction(row =>
                        {
                            if (row.DataItem.ID.ToString() == Request.QueryString["facilityID"])
                            {
                                row.DetailRow.Expanded = true;
                            }
                        })                       
                    )
                                 
                </text>);
            })
            .Render();
    })
    .Pageable()
    .Sortable()
    .DataSource(dataSource => dataSource
        .Server()
        .Model(model => model.Id(l => l.ID))
        .Read(read => read.Action("UserFacilities", "LabAdmin"))
    )
    .RowAction(row =>
    {
        if (row.DataItem.ID.ToString() == Request.QueryString["labID"])
        {
            row.DetailRow.Expanded = true;
        }
    })
    .Render();
}

How else should I code the third grid if I can't use the inline markup blocks?

Thanks,

Steve

0
Daniel
Telerik team
answered on 03 Jun 2013, 03:18 PM
Hello Stephen,

This is a limitation of the Razor view engine. Tags cannot be nested. In order to avoid the exception I can suggest to use a helper:

.DetailTemplate(f => myDetailDetailViewTemplate(this, f))
@helper myDetailDetailViewTemplate(WebViewPage page, MyModel f)
{
   @{
        Html.Kendo().TabStrip()
            .Name("ts" + f.ID)
            .SelectedIndex(0)
            .Items(childitems =>
            {
                childitems.Add().Text("User Facility Admins").Content(@<text>
                ...
                         
     
}

or the action overload of the Content method and the Render method for the inner controls:
Html.Kendo().TabStrip()
    .Name("ts" + f.ID)
    .SelectedIndex(0)
    .Items(childitems =>
    {
        childitems.Add().Text("User Facility Admins").Content(() =>
                Html.Kendo().Grid(f.Users_Roles)
                    ...
                    .Render()
            )


Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 05 Jun 2013, 04:06 PM
Hello,

Thanks for the info.  I tried the action overload of the Content method (which is what I would prefer to use) but I was getting multiple errors so I tried the helper method which is the one I have seen most frequently used.  I am getting an error there as well.  It says there is an argument missing on this line: 
@{ Html.Kendo().TabStrip()

Any ideas?

Thanks,

Steve

0
Stephen
Top achievements
Rank 1
answered on 05 Jun 2013, 07:31 PM
Ok so I changed it from being in a code block to the normal syntax and that worked:
@(Html.Kendo().TabStrip()
     .......
 )

But the problem I am having now is a similar issue I experienced previously with a 2-level grid.  Both levels of grid are editable but when I click and edit or add, before the popup would show the tab item would collapse.  This was fixed by adding a parameter that indicates which row should be expanded and expand it in the master Grid RowAction callback.

I have that done here as well but now that it is in the helper template it is not working, at least not reliably.

Here is my full view code:
@helper myDetailViewTemplate(WebViewPage page, PASSLibrary.User_Facilities f)
{
    @(Html.Kendo().TabStrip()
            .Name("tabstrip" + f.ID)
            .SelectedIndex(0)
            .Items(childitems =>
            {
                childitems.Add().Text("User Facility Admins").Content(
                    @<text>       
                        @(Html.Kendo().Grid(f.Users_Roles)
                            .Name("gridUserFacilityAdmins" + f.ID)
                            .Columns(columns =>
                            {
                                columns.Bound(a => a.User_ID).Template(a => a.User_ID.ToString()).Title("User ID");
                                columns.Bound(a => a.User.BNL_ID).Title("BNL ID");
                                columns.Bound(a => a.User.Pool.First_Name).Title("First Name");
                                columns.Bound(a => a.User.Pool.Last_Name).Title("Last Name");
                                columns.Bound(a => a.User.Account).Title("BNL Account");
                                columns.Command(command => { command.Destroy(); }).Width(50);
                            })
                            .ToolBar(toolbar => toolbar.Create())
                            .Editable(editable => editable.Mode(GridEditMode.PopUp))
                            .Sortable()
                            .DataSource(dataSource => dataSource
                                .Server()
                                .Model(model => model.Id(a => a.User_ID))
                                .Read(read => read.Action("UserFacilities", "LabAdmin", new { facilityID = f.ID }))
                                .Create(create => create.Action("AddUserFacilityAdmin", "LabAdmin"))
                                .Destroy(destroy => destroy.Action("DeleteUserFacilityAdmin", "LabAdmin"))
                            )
                        )
                    </text>
                );
            })
    )
}
 
@{ Html.Kendo().Grid(Model)
    .Name("gridLaboratories")
    .Columns(columns =>
    {
        columns.Bound(l => l.ID);
        columns.Bound(l => l.Description);
    })
    .DetailTemplate(
        @<text>                  
            @(Html.Kendo().Grid(item.User_Facilities)
                .Name("gridUserFacilities" + item.ID)
                .Columns(columns =>
                {
                    columns.Command(command => { command.Edit(); }).Width(50);
                    columns.Bound(f => f.ID);
                    columns.Bound(f => f.Description);
                    columns.Command(command => { command.Destroy(); }).Width(50);
                })
                .DetailTemplate(f => myDetailViewTemplate(this, f))
                .ToolBar(toolbar => toolbar.Create())
                .Editable(editable => editable.Mode(GridEditMode.PopUp))
                .Sortable()
                .DataSource(dataSource => dataSource
                    .Server()
                    .Model(model =>
                    {
                        model.Id(f => f.ID);
                        model.Field(field => field.ID).DefaultValue("");
                        model.Field(field => field.Description).DefaultValue("");
                    })
                    .Create(create => create.Action("AddUserFacility", "LabAdmin"))
                    .Read(read => read.Action("UserFacilities", "LabAdmin", new { labID = item.ID }))
                    .Update(update => update.Action("UpdateUserFacility", "LabAdmin"))
                    .Destroy(destroy => destroy.Action("DeleteUserFacility", "LabAdmin"))
                )
                .RowAction(row =>
                {
                    if (row.DataItem.ID.ToString() == Request.QueryString["facilityID"])
                    {
                        row.DetailRow.Expanded = true;
                    }
                })
            )
        </text>
    )
    .Pageable()
    .Sortable()
    .DataSource(dataSource => dataSource
        .Server()
        .Model(model => model.Id(l => l.ID))
        .Read(read => read.Action("UserFacilities", "LabAdmin"))
    )
    .RowAction(row =>
    {
        if (row.DataItem.ID.ToString() == Request.QueryString["labID"])
        {
            row.DetailRow.Expanded = true;
        }
    })
    .Render();
}

Can you see anything missing or does this not work when it is in a helper template?  If so, would it work using the other method of overloading the Content method?

Thanks for all of the help.

Steve




0
Daniel
Telerik team
answered on 07 Jun 2013, 12:24 PM
Hello Steve,

There should not be any difference if the Grid is added with a helper. Which row is not expanded correctly? If the "gridLaboratories" Grid row is not expanded then you should pass "labID" also with the "gridUserFacilityAdmins" read request. 

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 07 Jun 2013, 01:27 PM
Hi,

Yes it is the "gridLaboratories".  What would the syntax be for sending multiple route values?  I see the read.Action overload can take a routeDictionary instead of object type for the route values so maybe that's what to do but I'm not sure how to form that.

Currently I have:
.Read(read => read.Action("UserFacilities", "LabAdmin", new { facilityID = f.ID }))
Thanks,

Steve

0
Daniel
Telerik team
answered on 10 Jun 2013, 01:07 PM
Hello Steve,

You should pass the ID as parameter to the helper and then add it to the "gridUserFacilityAdmins" Grid route values:

.DetailTemplate(f => myDetailViewTemplate(this, f, item.ID))
@helper myDetailViewTemplate(WebViewPage page, PASSLibrary.User_Facilities f, int labID){
        ...           
        .Read(read => read.Action("UserFacilities", "LabAdmin", new { facilityID = f.ID, labID  =  labID}))
Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 11 Jun 2013, 05:50 PM
perfect thanks!
0
Stephen
Top achievements
Rank 1
answered on 12 Jun 2013, 06:30 PM
Another question :)

In this same scenario with the nested grids, in the popup editor of the second grid, how can I show the popup editor without the field for the primary key from the first grid?

For example, I have my main grid listing Laboratories and my nested grid showing the Facilities in that Laboratory.  So once I have expanded a particular Laboratory and I want to add a new Facility, I already now what the Laboratory_ID is for this new Facility since I am in that grid.  I can set the default value for that field in the model (as shown below) but how do I not show that field in the editor?
.DataSource(dataSource => dataSource
    .Server()
    .Model(model =>
    {
        model.Id(f => f.ID);
        model.Field(field => field.ID).DefaultValue("");
        model.Field(field => field.Description).DefaultValue("");
        model.Field(field => field.Laboratory_ID).DefaultValue(item.ID);
    })
Thanks again,

Steve



0
Daniel
Telerik team
answered on 14 Jun 2013, 12:36 PM
Hello Steve,

In PopUp editing you could use an attribute on the property (ScaffoldColumn or HiddenInput) or create a custom editor template as demonstrated in this code-library in order to not show an editor for a field.

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 14 Jun 2013, 02:47 PM
Ok I am able to do this with the custom editor template but then since the Laboratory_ID field is not there it is not passing the value back.  I thought if I had the default value set in the grid then it would pass it but I guess when you use the custom editor and leave out that field it doesn't do that.  So in this case, how do I get it to update the model with the Laboratory_ID?  DO I have to modify my Controller method and manually pass it as a parameter?

Currently my datasource on the grid looks like this:
.DataSource(dataSource => dataSource
    .Server()
    .Model(model =>
    {
        model.Id(f => f.ID);
        model.Field(field => field.ID).DefaultValue("");
        model.Field(field => field.Description).DefaultValue("");
        model.Field(field => field.Laboratory_ID).DefaultValue(item.ID);
    })
    .Create(create => create.Action("AddUserFacility", "LabAdmin"))
    .Read(read => read.Action("UserFacilities", "LabAdmin", new { labID = item.ID }))
    .Update(update => update.Action("UpdateUserFacility", "LabAdmin"))
    .Destroy(destroy => destroy.Action("DeleteUserFacility", "LabAdmin"))
)
and my Controller method looks like this:
[Authorize(Roles = "Lab_Admin")]
[HttpPost]
public ActionResult AddUserFacility(User_Facilities user_facility)
{
    try
    {
        using (PASSEntities context = new PASSEntities())
        {
            context.User_Facilities.Add(user_facility);
            context.SaveChanges();
        }
        return RedirectToAction("UserFacilities");
    }
    catch
    {
        return View();
    }
}
And here is my custom editor template:
@model PASSLibrary.User_Facilities
 
@Html.HiddenFor(model => model.ID)
 
<div class="editor-label">
    @Html.Label("ID")
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.ID)
    @Html.ValidationMessageFor(model => model.ID)
</div>
 
<div class="editor-label">
    @Html.Label("Description")
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Description)
    @Html.ValidationMessageFor(model => model.Description)
</div>


0
Daniel
Telerik team
answered on 18 Jun 2013, 10:42 AM
Hello,

If you do not wish to show the "Laboratory_ID" and it is needed to send it to the server then you should use a hidden input:

@Html.HiddenFor(model => model.ID)
@Html.HiddenFor(model => model.Laboratory_ID)

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 18 Jun 2013, 06:48 PM
Hi Daniel,

Thanks for that.  I added the hidden to the custom edit template and it works for me on the edit but on the add I get an error that it cannot find the view, which of course is there, so it seems like it is maybe not passing the Laboratory_ID for the add?  Any other insight on that?  My code is still the same as above with of course the hidden added to the editor template.

Thanks,

Steve



0
Daniel
Telerik team
answered on 20 Jun 2013, 02:48 PM
Hello Steve,

I am not sure what could be causing this problem. Even if the parameter is not passed the exception should be different. Could you provide the code for the controller so I can check the exact setup?

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 25 Jun 2013, 01:08 PM
Sure thanks.  Here is the controller code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PASSLibrary;
 
namespace PASSAdmin.Controllers
{
    public class LabAdminController : Controller
    {
 
        public ActionResult Index()
        {
            return View();
        }
         
        #region User Facilities
 
        [Authorize(Roles = "Lab_Admin")]
        public ActionResult UserFacilities()
        {
 
            var context = new PASSEntities();
 
            IEnumerable<Laboratory> labList = (from l in context.Laboratories
                                    join ur in context.Users_Roles on l.ID equals ur.Laboratory_ID
                                    join u in context.Users on ur.User_ID equals u.ID
                                    join r in context.Roles on ur.Role_ID equals r.ID
                                    where u.Account == User.Identity.Name.Substring(4) && r.Name == "Lab_Admin"
                                    select l).ToList();
 
            return View(labList);
        }
 
        [Authorize(Roles = "Lab_Admin")]
        [HttpPost]
        public ActionResult AddUserFacility(User_Facilities user_facility)
        {
            try
            {
                using (PASSEntities context = new PASSEntities())
                {
                    context.User_Facilities.Add(user_facility);
                    context.SaveChanges();
                }
                return RedirectToAction("UserFacilities");
            }
            catch
            {
                return View();
            }
        }
 
        [Authorize(Roles = "Lab_Admin")]
        [HttpPost]
        public ActionResult DeleteUserFacility(string id, User_Facilities user_facility)
        {
            try
            {
                using (PASSEntities context = new PASSEntities())
                {
                    context.Entry(user_facility).State = System.Data.EntityState.Deleted;
                    context.SaveChanges();
                }
                return RedirectToAction("UserFacilities");
            }
            catch
            {
                return View();
            }
        }
 
        [Authorize(Roles = "Lab_Admin")]
        [HttpPost]
        public ActionResult UpdateUserFacility(string id, User_Facilities user_facility)
        {
            try
            {
                using (PASSEntities context = new PASSEntities())
                {
                    context.Entry(user_facility).State = System.Data.EntityState.Modified;
                    context.SaveChanges();
                    return RedirectToAction("UserFacilities");
                }
            }
            catch
            {
                return View();
            }
        }
        #endregion
 
        #region User Facility Admins
 
        [Authorize(Roles = "Lab_Admin")]
        [HttpPost]
        public ActionResult AddUserFacilityAdmin(Users_Roles user_role)
        {
            try
            {
                using (PASSEntities context = new PASSEntities())
                {
                    context.Users_Roles.Add(user_role);
                    context.SaveChanges();
                }
                return RedirectToAction("UserFacilities");
            }
            catch
            {
                return View();
            }
        }
 
        [Authorize(Roles = "Lab_Admin")]
        [HttpPost]
        public ActionResult DeleteUserFacilityAdmin(int id, Users_Roles user_role)
        {
            try
            {
                using (PASSEntities context = new PASSEntities())
                {
                    context.Entry(user_role).State = System.Data.EntityState.Deleted;
                    context.SaveChanges();
                }
                return RedirectToAction("UserFacilities");
            }
            catch
            {
                return View();
            }
        }
        #endregion
 
    }
}

Please let me know if you need anything else.  Obviously there is an issue since its not working, but I think as you pointed out I am also having another problem in my solution with the error messages.  I have client side validation enabled and unobtrusive javascript enabled in my web config but I keep getting errors like I have described when I should be getting other error messages.

0
Daniel
Telerik team
answered on 27 Jun 2013, 01:40 PM
Hello Steve,

From the code it seems that the described problem will occur if an exception is thrown when saving the changes. A ViewResult for the current action is returned when an exception is thrown and there does not seem to be a view named "AddUserFacility". Could you check if an exception is thrown? 

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 27 Jun 2013, 07:59 PM
An exception is thrown but I am not handling it properly (a separate problem).  When I debug and check the exception it tells me that "The ID field is required."  When I don't use a custom editor template it works fine.  So somehow with the custom editor template it is not seeing that the ID field is filled in....

Here is my custom editor template again:
@model PASSLibrary.User_Facilities
 
@Html.HiddenFor(model => model.ID)
@Html.HiddenFor(model => model.Laboratory_ID)
 
<div class="editor-label">
    @Html.Label("ID")
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.ID)
    @Html.ValidationMessageFor(model => model.ID)
</div>
 
<div class="editor-label">
    @Html.Label("Description")
</div>
<div class="editor-field">
    @Html.TextBoxFor(model => model.Description, new { style = "width: 250px;" })
    @Html.ValidationMessageFor(model => model.Description)
</div>
0
Daniel
Telerik team
answered on 01 Jul 2013, 01:55 PM
Hello Steve,

If the value for the ID field should be entered  by the user then you should remove the hidden input. Otherwise, the ModelBinder will always populate the value from the hidden input because it is first.

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 02 Jul 2013, 03:12 PM
Hi,

Ok I understand what you are saying and that makes sense to me when it is in "add" mode but what about when it is in "edit" mode.  It uses the same custom edit template so doesn't the hidden need to be there for the "edit"?

Thanks,

Steve
0
Daniel
Telerik team
answered on 04 Jul 2013, 09:29 AM
Hello Steve,

If the user should enter the value only when creating a new item then you could check the Id value and render only the HiddenInput or the editor e.g.

@if (!String.IsNullOrEmpty(Model.ID))
{
    @Html.HiddenFor(model=> model.ID)
}
else
{
    <div class="editor-label">
        @Html.Label("ID")
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.ID)
        @Html.ValidationMessageFor(model => model.ID)
    </div>
}
Generally speaking, the ID of the model should assigned on the server and not by the user.

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 08 Jul 2013, 07:13 PM
Ok that makes sense.

In this particular case though I don't think I need to do that because I am using jquery to disable the ID field if it is an edit.

Thanks!
0
Stephen
Top achievements
Rank 1
answered on 16 Jul 2013, 04:05 PM

Ok posting one more time in this thread...hopefully.

I've got everything working now the way I want but there are two small things happening that I would like to fix.

1.  When I add or delete an item from the nested grid, the parent grid collapses instead of remaining open.  I thought I had fixed this issue early on but it seems to be happening again now.

2.  When I click "Delete" on the nested grid, I get the "Delete this item" pop up twice.

Here is my view code:

@{ Html.Kendo().Grid(Model)
    .Name("gridLaboratories")
    .Columns(columns =>
    {
        columns.Command(command => { command.Edit(); }).Width(50);
        columns.Bound(l => l.ID);
        columns.Bound(l => l.Description);
        columns.Command(command => { command.Destroy(); }).Width(50);
    })
    .DetailTemplate(l =>
    {
        Html.Kendo().TabStrip()
            .Name("tabstrip" + l.ID)
            .SelectedIndex(0)
            .Items(items =>
            {
                items.Add().Text("Lab Admins").Content(@<text>       
 
                    @(Html.Kendo().Grid(l.Users_Roles)
                        .Name("gridLabAdmins" + l.ID)
                        .Columns(columns =>
                        {
                            columns.Bound(a => a.User.BNL_ID).Title("BNL ID");
                            columns.Bound(a => a.User.Pool.First_Name).Title("First Name");
                            columns.Bound(a => a.User.Pool.Last_Name).Title("Last Name");
                            columns.Bound(a => a.User.Account).Title("BNL Account");
                            columns.Command(command => { command.Destroy(); }).Width(50);
                        })
                        .ToolBar(toolbar => toolbar.Create())
                        .Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("SystemAdmin/LabAdmin"))
                        .Sortable()
                        .DataSource(dataSource => dataSource
                            .Server()
                            .Model(model => model.Id(a => a.ID))
                            .Read(read => read.Action("Laboratories", "SystemAdmin", new { labID = l.ID }))
                            .Create(create => create.Action("AddLabAdmin", "SystemAdmin"))
                            .Destroy(destroy => destroy.Action("DeleteLabAdmin", "SystemAdmin"))
                        )
                    )
 
                </text>);
            })
            .Render();
    })
    .ToolBar(toolbar => toolbar.Create())
    .Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("SystemAdmin/Laboratory"))
    .Pageable()
    .Sortable()
    .DataSource(dataSource => dataSource
        .Server()
        .Model(model =>
        {
            model.Id(l => l.ID);
            model.Field(field => field.ID).DefaultValue("");
            model.Field(field => field.Description).DefaultValue("");
        })
        .Create(create => create.Action("AddLaboratory", "SystemAdmin"))
        .Read(read => read.Action("Laboratories", "SystemAdmin"))
        .Update(update => update.Action("UpdateLaboratory", "SystemAdmin"))
        .Destroy(destroy => destroy.Action("DeleteLaboratory", "SystemAdmin"))
    )
    .RowAction(row =>
    {
        if (row.DataItem.ID.ToString() == Request.QueryString["labID"])
        {
            row.DetailRow.Expanded = true;
        }
    })
    .Render();
}
 
<script type="text/javascript">
$(document).ready(function () {
    var gridMode = getURLParameter("gridLaboratories-mode");
    if (gridMode == "edit") {
        $("#gridLaboratoriesPopUp").find('input[name="ID"]').attr("disabled", true);
    }
});
 
function getURLParameter(name) {
    return decodeURI((RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || [, null])[1]);
}
</script>

And here is my controller code:

public class SystemAdminController : Controller
{
 
    public ActionResult Index()
    {
        return View();
    }
 
    #region Laboratories
 
    [Authorize(Roles = "System_Admin")]
    public ActionResult Laboratories()
    {
        var context = new PASSEntities();
        return View(context.Laboratories.ToList());
    }
 
    [Authorize(Roles = "System_Admin")]
    [HttpPost]
    public ActionResult AddLaboratory(Laboratory laboratory)
    {
        try
        {
            using (PASSEntities context = new PASSEntities())
            {
                context.Laboratories.Add(laboratory);
                context.SaveChanges();
            }
            return RedirectToAction("Laboratories");
        }
        catch
        {
            return View();
        }
    }
 
    [Authorize(Roles = "System_Admin")]
    [HttpPost]
    public ActionResult DeleteLaboratory(string id, Laboratory laboratory)
    {
        try
        {
            using (PASSEntities context = new PASSEntities())
            {
                context.Entry(laboratory).State = System.Data.EntityState.Deleted;
                context.SaveChanges();
            }
            return RedirectToAction("Laboratories");
        }
        catch
        {
            return View();
        }
    }
 
    [Authorize(Roles = "System_Admin")]
    [HttpPost]
    public ActionResult UpdateLaboratory(string id, Laboratory laboratory)
    {
        try
        {
            using (PASSEntities context = new PASSEntities())
            {
                context.Entry(laboratory).State = System.Data.EntityState.Modified;
                context.SaveChanges();
                return RedirectToAction("Laboratories");
            }
        }
        catch
        {
            return View();
        }
    }
    #endregion
 
    #region Lab Admins
 
    public class EmployeeInfo
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public decimal UserID { get; set; }
    };
 
    public JsonResult GetEmployeeByBNLID(string bnlID)
    {
        var context = new PASSEntities();
        User employee = context.Users.SingleOrDefault(user => user.BNL_ID == bnlID);
 
        EmployeeInfo employeeInfo = new EmployeeInfo
        {
            FirstName = employee.Pool.First_Name,
            LastName = employee.Pool.Last_Name,
            UserID = employee.ID
        };
 
        return Json(employeeInfo);
    }
 
    [Authorize(Roles = "System_Admin")]
    [HttpPost]
    public ActionResult AddLabAdmin(Users_Roles user_role)
    {
        try
        {
            using (PASSEntities context = new PASSEntities())
            {
                context.Users_Roles.Add(user_role);
                context.SaveChanges();
            }
            return RedirectToAction("Laboratories");
        }
        catch
        {
            return View();
        }
    }
 
    [Authorize(Roles = "System_Admin")]
    [HttpPost]
    public ActionResult DeleteLabAdmin(Users_Roles user_role)
    {
        try
        {
            using (PASSEntities context = new PASSEntities())
            {
                context.Entry(user_role).State = System.Data.EntityState.Deleted;
                context.SaveChanges();
            }
            return RedirectToAction("Laboratories");
        }
        catch
        {
            return View();
        }
    }
    #endregion
}









0
Daniel
Telerik team
answered on 18 Jul 2013, 12:51 PM
Hello,

The destroy command does not use the Read action since the Grid does not need to be rendered again and so the ID should also be sent with the destroy request:

.Destroy(destroy => destroy.Action("DeleteLabAdmin", "SystemAdmin", new { labID = l.ID }))
public ActionResult DeleteLabAdmin(Users_Roles user_role, int labID)
{
    try
    {
        using (PASSEntities context = new PASSEntities())
        {
            context.Entry(user_role).State = System.Data.EntityState.Deleted;
            context.SaveChanges();
        }
        return RedirectToAction("Laboratories", new {labID = labID});
    }
The most likely reason for the problem with the delete confirmation is that the event was not stopped in the earlier versions of the widgets. Which version are you currently using? Could you check if updating to the latest release resolves the problem? Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 18 Jul 2013, 07:37 PM
Ok thanks.  That worked for keeping the nested grid open after delete.  I was having the same issue with the Add for the nested grid (and for Add, Update, and Delete on a different view) so I added the same code and that worked there as well so does that mean that the create and edit commands also do not use the Read action?  And just so I understand exactly what is happening, is the added code for the Add, Create, and Delete actions forcing the grid to expand those rows because it is just a pass-through in the controller so when I redirect back to the grid that info will be on the querystring so the row action can use it to expand the proper row.
.RowAction(row =>
{
    if (row.DataItem.ID.ToString() == Request.QueryString["labID"])
    {
        row.DetailRow.Expanded = true;
    }
})

As for the double delete confirmation, I have the latest version of the Kendo MVC controls 2013.Q2.  Note that it only happens with the nested grid and not the parent grid.  Is it somehow referencing the delete from the parent grid?
0
Daniel
Telerik team
answered on 22 Jul 2013, 03:38 PM
Hello Steve,

The read action should be used for edit and create to render the Grid again in edit mode. After updating or creating the item however, the Update and Create actions are used so the ID should again be added to the route values.
I reproduced the problem with the double confirmation and it seems that the event is not stopped when server editing is used. We will look into it and will fix it for one of the next internal builds.

Regards,
Daniel
Telerik
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
Stephen
Top achievements
Rank 1
answered on 22 Jul 2013, 04:03 PM
Great thank you!
0
Andrew
Top achievements
Rank 1
answered on 01 Jul 2015, 06:30 PM

Hello , i am faced with a similar problem  where my 3rd level grid seems to overwrite my second level..

 Your solution , you mentioned the dynamic naming , can you tell me how i can get his done?

 

Thanks in advance...

0
Daniel
Telerik team
answered on 03 Jul 2015, 10:50 AM
Hello Andrew,

Could you provide the code that you are using so I can check the setup? I would also suggest to check this code-library project which demonstrates hierarchy with 3 levels.

Regards,
Daniel
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
jay taylor
Top achievements
Rank 1
answered on 30 Sep 2015, 07:12 PM
In case anybody looking at this is in an early stage where the technology hasn't yet been chosen, consider this: The Telerik asp.net grid does hierarchies with very little code, using a wizard.  Works fine. Just a bit of postback time. 
0
Trushar
Top achievements
Rank 1
Veteran
answered on 19 Dec 2016, 10:28 PM

hi Mithun, can you please provide what exactly you have changed ? because i am also facing slimier issue, my third grid is binding data from second grid. i couldn't figure out why its happening .

Thanks,Trushar

0
Ahan
Top achievements
Rank 1
answered on 21 Sep 2020, 07:32 PM
Hi has anyone solved this issue in Angular 6+ with Kendo UI grid? I am trying to dynamically nest n-level deep grids without hardcoding ng-template over and over. 
Tags
Grid
Asked by
Mithun
Top achievements
Rank 1
Answers by
Vladimir Iliev
Telerik team
Mithun
Top achievements
Rank 1
Stephen
Top achievements
Rank 1
Daniel
Telerik team
Andrew
Top achievements
Rank 1
jay taylor
Top achievements
Rank 1
Trushar
Top achievements
Rank 1
Veteran
Ahan
Top achievements
Rank 1
Share this question
or