Creating Grid with unknown columns

7 posts, 1 answers
  1. Ruairi
    Ruairi avatar
    64 posts
    Member since:
    Nov 2013

    Posted 11 Feb 2016 Link to this post

    I have two entities Competencies and Roles. These are both created by the user and stored in the database.

    On a specific screen the user select certain Competencies and certain Roles. These then have to be displayed in a matrix.

    They will then use check boxes to link the Competencies and Roles.
    Each competency will be linked to several of the roles.
    This matrix is completely dynamic as I do not know how many columns there will be or the names of the columns.

    I know that there will be a Competency Id column and a Competency Name column. Each row will have one or more roles.
    We use kendo grids throughout the application so I want to use a kendo grid.

    I have tried many different ways of doing this. I have successfully passed in a System.Data.DataTable which works but is cumbersome to create. We use NHibernate and it is easier to create a model. The most successful way that I have that uses a model is as follows:

    public class BasicRowModel
      {
        public string CompetencyId { get; set; }
        public string CompetencyName { get; set; }
        public List<BasicTrainingRoleModel> Roles { get; set; }
      }

    public class BasicTrainingRoleModel
      {
        public string RoleName { get; set; }
        public bool RoleVal { get; set; }
      }

    My view is as follows:
    @model IEnumerable<BasicRowModel>
    @using Models
    @using System.Linq;

    @{
        ViewBag.Title = "DynamicGrid2";
    }

    <h2>DynamicGrid2</h2>

    @section scripts
    {
      <script type="text/javascript" src="@Url.Content("~/Scripts/Training.js")"></script>

    }

    @{
      ViewBag.Title = "Administration";
      Layout = "~/Views/Shared/_Layout.cshtml";
    }


    @(Html.Kendo().Grid(Model)
      .Name("RoleCompsTable")
      .Columns(columns =>
      {
        columns.Bound(r => r.CompetencyId)
          .Hidden();
        columns.Bound(r => r.CompetencyName)
          .Title("Competency");
        for (int c = 0; c < ViewBag.RoleCount; c++)
        {
          //columns.Template(@<text>@item.Roles[c].RoleVal.ToString()</text>).Title(ViewBag.Roles[c]);
          columns.Bound(r => r.Roles[c].RoleVal).Title(ViewBag.Roles[c]);
        }
        //columns.Bound(r => r.Roles[0].RoleVal).Title(ViewBag.Roles[0]);
        //columns.Bound(r => r.Roles[1].RoleVal).Title(ViewBag.Roles[1]);
        //columns.Bound(r => r.Roles[2].RoleVal).Title(ViewBag.Roles[2])

      }))

    The for loop does not work with either the columns.Bound or the template. It results in an out of range error. When stepping into this code it works fine but it is afterwards that the error occurs.
    Exception Details: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
    Parameter name: index

    Source Error:

    Line 31:     for (int c = 0; c < ViewBag.RoleCount; c++)
    Line 32:     {
    Line 33:columns.Template(@<text>@item.Roles[c].RoleVal.ToString()</text>).Title(ViewBag.Roles[c]);
    Line 34:       //columns.Bound(r => r.Roles[c].RoleVal).Title(ViewBag.Roles[c]);
    Line 35:     }


    When I comment out the for loop and uncomment the columns.Bound lines it works perfectly with the attached result. Obviously this is not a solution for me as it is dynamic and I do not know how many columns there will be. Why does the for loop not work for this?



    Regards
    Tyler  

  2. Rosen
    Admin
    Rosen avatar
    3253 posts

    Posted 15 Feb 2016 Link to this post

    Hello Tyler,

    The cause for the error is how the C# captures scope variable. In the code you have pasted, the c variable value is incremented thus all of the expression used in the column Bound method will point to the last value of the c. In order to correct this you should use a local variable which will hold the exact value of the c at the point of creation of the Bound method. Similar to the following:

    for (int c = 0; c < ViewBag.RoleCount; c++)
    {
      var idx = c;
      columns.Bound(r => r.Roles[idx].RoleVal).Title(ViewBag.Roles[c]);
    }

    Regards,
    Rosen
    Telerik
     
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
     
  3. Ruairi
    Ruairi avatar
    64 posts
    Member since:
    Nov 2013

    Posted 18 Feb 2016 Link to this post

    Thank you Rosen. That works perfectly.

    I am having a problem with binding to the ClientTemplate. If I don't use a template the data binds fine with "true" and "false" for the Boolean values. If I use a ClientTemplate to show the values as a checkbox the values don't show on the grid.

    for (int c = 0; c < ViewBag.RoleCount; c++)
    {
    var idx = c;
    columns.Bound(r => r.Roles[idx].RoleVal)
    .ClientTemplate("#<input type='checkbox' #= RoleVal ? checked='checked' : '' # disabled='disabled' ></input>#")
    .Title(ViewBag.Roles[c]);
    }

    Would appreciate your help.

    Tyler

  4. Rosen
    Admin
    Rosen avatar
    3253 posts

    Posted 18 Feb 2016 Link to this post

    Hello Tyler,

    I guess the it does not work due to incorrect template. The hashes designate code expression, thus you should not wrap the entire markup with them.

    "<input type='checkbox' #= RoleVal ? checked='checked' : '' # disabled='disabled' ></input>"

    Information on Kendo templates can be found here. And information about Column's ClientTemplate is located here.

    Regards,
    Rosen
    Telerik
     
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
     
  5. Ruairi
    Ruairi avatar
    64 posts
    Member since:
    Nov 2013

    Posted 19 Feb 2016 in reply to Rosen Link to this post

    Rosen,

    You are absolutely right, I shouldn't have those hashes in. However I have tried it as you suggested and numerous other ways. It doesn't seem to bind to the RoleVal in the ClientTemplate. I have checkboxes elsewhere in grids with no problem. The difference here I think is that they are bound to a list. I have read the documentation and numerous posts and cannot seem to figure a way to bind to the clientTemplate. Can you point me in the right direction? Is it even possible given the fact that the model is essentially dynamic?

    Appreciate your help.

     

    Tyler

  6. Answer
    Rosen
    Admin
    Rosen avatar
    3253 posts

    Posted 22 Feb 2016 Link to this post

    Hello Tyler,

    In order to access the same value as with the column, you should use the same "path" as the one used in the Column.Bound configuration. Something similar to the following:

    "<input type='checkbox' #= Roles[" + idx + "].RoleVal ? 'checked=checked' : '' # disabled='disabled' ></input>"

    Regards,
    Rosen
    Telerik
     
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
     
  7. Ruairi
    Ruairi avatar
    64 posts
    Member since:
    Nov 2013

    Posted 22 Feb 2016 in reply to Rosen Link to this post

    Thank you Rosen, works perfectly.
Back to Top