Grid with columns tied to a Dictionary (Sorting & Dropdown Menu)

7 posts, 1 answers
  1. Siarhei
    Siarhei avatar
    5 posts
    Member since:
    Jan 2016

    Posted 25 Jan Link to this post

    There are several topics on this forum regarding linking Grid columns to Dictionary properties. I've managed to do just that, but these columns are missing sorting and dropdown menu.

    My model represents an issue from issue tracking system that may have very different fields depending on particular configuration. I can't see it feasible to define each possible property for my model class and the only logical solution is to have direct properties for most common fields and have a Dictionary for custom fields.

     

    I'm using Kendo.MVC version 2016.1.112 in MVC 6 / .NET 5 project (which currently doesn't support server-side binding as far as I understand - rendering some solutions posted on this forum inapplicable). Browser is Chrome, but it doesn't seem the issue relates to browser version.

     

    My model class is as follows:

    public class Issue
    {
        public string Key { get; set; }
        public string Summary { get; set; }
        public string Status { get; set; }
        public string StatusType { get; set; }
        public DateTime? Created { get; set; }
        public DateTime? LastUpdated { get; set; }
     
        public Dictionary<string, object> CustomProperties { get; set; } = new Dictionary<string, object>();
    }

     

    The Grid is then defined in View's *.cshtml file as this:

    @(Html.Kendo().Grid<JIRAReports.Models.Issue>()
      .Name("grid")
      .HtmlAttributes(new { style = "margin-top: 10px;" })
      .DataSource(dataSource => dataSource.Ajax()
                                          .Sort(sort => sort.Add("LastUpdated").Ascending())
                                          .Read(read => read.Action("JiraIssues_Read", "Reports"))
                                          .PageSize(15)
     
                  )
      .Columns(columns =>
      {
        columns.Bound(issue => issue.Key).Width(120).HtmlAttributes(new { style = "white-space: nowrap;" })
          .ClientTemplate($"<a href=\"{baseIssueUrl}#=Key#\" target=\"_blank\">#=Key#</a>");
        columns.Bound(issue => issue.Status).Width(150)
          .ClientTemplate(statusColumnTemplate);
        columns.Bound(issue => issue.Summary).HtmlAttributes(new { style = "white-space: nowrap;" });
        columns.Bound(issue => issue.LastUpdated).Width(180).HtmlAttributes(new { style = "white-space: nowrap;" })
          .Title("Last Updated").Format("{0:M/d/yyyy (ddd)}");
        //Adding custom properties
        columns.Template("#:CustomProperties['CustomProperty1']#").Title("Custom Property 1");
        columns.Template("#:CustomProperties['CustomProperty2']#").Title("Custom Property 2").Hidden();
      })
      .Pageable(config => config.PageSizes(new[] { 10, 15, 25, 100 })
                                .Refresh(true))
      .Sortable(config => config.AllowUnsort(false).SortMode(GridSortMode.MultipleColumn))
      .Scrollable(config => config.Height("895px"))
      .Resizable(config => config.Columns(true))
      .ClientDetailTemplateId("item-template")
      .ToolBar(tools => tools.Custom().Text("Expand All").Name("ExpandAll"))
      .ToolBar(tools => tools.Custom().Text("Collapse All").Name("CollapseAll"))
      .NoRecords()
      .ColumnMenu()
      .Reorderable(config => config.Columns(true))
      .Deferred()

     

    The resulting grid is rendered as on the screenshot attached. You can see there is no dropdown next to columns linked to Dictionary's items, they are not sortable, but these columns do appear in dropdowns for other columns so they can be shown/hidden. But sorting on them is absolutely required.

     

    Please suggest a solution. There must be a way to link to less structured data or indexer property, especially since all we need is to display it in read-only mode.

  2. Siarhei
    Siarhei avatar
    5 posts
    Member since:
    Jan 2016

    Posted 25 Jan in reply to Siarhei Link to this post

    A little bit more information after trying something suggested in post: http://www.telerik.com/forums/dictionary-keys-binding

     

    First of all, suggested first option for Ajax binding to call "columns.Template(o => { }).ClientTemplate(...)" doesn't work as there is no Template method with lambda expression.

     

    Another suggested option to use "dynamic" as a model was somewhat successful. The code is like this:

    @(Html.Kendo().Grid<dynamic>()
      .Name("grid")
      .HtmlAttributes(new { style = "margin-top: 10px;" })
      .DataSource(dataSource => dataSource.Ajax()
                                          .Sort(sort => sort.Add("LastUpdated").Ascending())
                                          .Read(read => read.Action("JiraIssues_Read", "Reports"))
                                          .PageSize(15)
     
                  )
      .Columns(columns =>
      {
        columns.Bound("Key").Width(120).HtmlAttributes(new { style = "white-space: nowrap;" })
          .ClientTemplate($"<a href=\"{jiraUrl}#=Key#\" target=\"_blank\">#=Key#</a>");
        columns.Bound("Status").Width(150)
          .ClientTemplate(statusColumnTemplate);
        columns.Bound("Summary").HtmlAttributes(new { style = "white-space: nowrap;" });
        columns.Bound(typeof(DateTime), "LastUpdated").Width(180).HtmlAttributes(new { style = "white-space: nowrap;" })
          .Title("Last Updated").Format("{0:M/d/yyyy (ddd)}");
        //Adding custom properties
        columns.Bound("CustomProperties['CustomProperty1']").Title("Custom Property 1");
        columns.Bound("CustomProperties['CustomProperty2']").Title("Custom Property 2").Hidden();
      })

     

    The custom properties can now be sorted on and I can see how I can achieve quite a bit more with dynamic as a model (albeit with some convenience lost). However, it seem like with dynamic option all data is treated as string. For example, I could not make the LastUpdated field display as a Date using Format method (even after calling Bound with specific type of DateTime).

     

    Help is much appreciated.

  3. UI for ASP.NET MVC is VS 2017 Ready
  4. Siarhei
    Siarhei avatar
    5 posts
    Member since:
    Jan 2016

    Posted 26 Jan in reply to Siarhei Link to this post

    OK, seems the following is working for formatting DateTime columns when grid uses dynamic model:

    columns.Bound("LastUpdated").Width(180).HtmlAttributes(new { style = "white-space: nowrap;" })
      .ClientTemplate("#=kendo.toString(kendo.parseDate(LastUpdated), 'M/dd/yyyy (ddd)')#")
      .Title("Last Updated");

    Still, please let me know if the solutions in my posts are optimal or there is some better way of doing this.

  5. Alexander Popov
    Admin
    Alexander Popov avatar
    1416 posts

    Posted 28 Jan Link to this post

    Hello Siarhei,

    The reason for this behavior is the inability of the Kendo UI DataSource to resolve the data type of complex objects' properties. As a result, the date strings are not parsed into Date objects. The same happens when using a dynamic model and not explicitly specifying the data type. Here is an example how this can be done using a Custom DataSource: 
    .DataSource(d => d
        .Custom()
           ...
        .Schema(s => s.
            Model(m => {
                m.Field("SomeDate", typeof(DateTime));
            })
        )
    )
    The approach where the date string is parsed manually in a template will work as well, however that way you lose the ability to sort and filter the column properly.

    Regards,
    Alexander Popov
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  6. Siarhei
    Siarhei avatar
    5 posts
    Member since:
    Jan 2016

    Posted 28 Jan in reply to Alexander Popov Link to this post

    Alexander,

     

    Thanks for the suggestion. I would like to first comment on your last sentence. So far I haven't got any issues with sorting on such columns and I can't see how it could be an issue given the fact that sorting is done server-side and Controller's method receives proper DataSourceRequest parameter.

     

    Regarding Custom datasource, I did manage to make it work ...somewhat. First of all it took few tries to get it right, the following resource has proved helpful: http://docs.telerik.com/kendo-ui/aspnet-mvc/custom-datasource

     

    So far I have the following setup that I'm having an issue with:

    .DataSource(dataSource => dataSource.Custom()
                                        .Type("aspnetmvc-ajax")
                                        .PageSize(15)
                                        .ServerPaging(true)
                                        .ServerSorting(true)
                                        .ServerFiltering(true)
                                        .Transport(transport => transport
                                            .Read(read => read.Action("JiraIssues_Read", "Reports"))
                                        )
                                        .Schema(schema => schema
                                          .Data("Data")
                                          .Total("Total")
                                          .Errors("Errors")
                                          .Model(model => {
                                            model.Field("LastUpdated", typeof(DateTime));
                                            model.Field("Resolved", typeof(DateTime));
                                            model.Field("CustomProperties['Target Completion']", typeof(DateTime));
                                          })
                                        )

     

    Columns are then added by reading CustomProperties configuration and set in the foreach loop with the following code:

    var columnBuilder = columns.Bound($"CustomProperties['{fieldConfig.Name}']");
    columnBuilder.Title(fieldConfig.Name);
    if (fieldConfig.IsHidden)
    {
      columnBuilder.Hidden();
    }
    if (fieldConfig.Type == "Date")
    {
      columnBuilder.Format("{0:M/dd/yyyy (ddd)}");
    }

    I know for a fact that this code adds "CustomProperties['Target Completion']" column with Format as specified, but the column value is not formatted as expected and in default format, e.g. "2016-01-20T00:00:00". Meanwhile "LastUpdated" and "Resolved" columns display as expected, with Format applied.

     

    When looking at generated page source, I see that column is defined with "field" of "CustomProperties[\u0027Target Completion\u0027]" and model defines "CustomProperties['Target Completion']":{"type":"date"}.

    Not sure if difference between \u0027 and ' causes this or grid can't apply formatting to complex objects' fields even if model specifically sets it. If the latter is the case, I don't see why it makes sense to use Custom DataSource as it makes code longer.

     

    Please let me know if there is a way to properly support DateTime type for columns for complex fields like CustomProperties['...'].

  7. Answer
    Alexander Popov
    Admin
    Alexander Popov avatar
    1416 posts

    Posted 01 Feb Link to this post

    Hello,

    You are correct, the sorting should not be affected. In such cases we usually recommend flattening the ViewModel, although using the "from" option to create a new field is also an option. For example: 
    @(Html.Kendo().Grid<dynamic>()
        .Name("Grid")
        .Columns(columns =>
        {
            columns.Bound(typeof(DateTime), "SomeDate").Width(200).Format("{0:g}");
        })
        .Filterable()
        .Sortable()
        .DataSource(dataSource => dataSource
            .Custom()
            ....
            .Schema(s =>
            {
                s.Data("Data");
                s.Model(m =>
                {
                    m.Field("SomeDate", typeof(DateTime)).Parse(@<text>function(dateString) { return kendo.parseDate(dateString); }</text>).From("SomeObject.SomeDate");
                });
            })
            .Transport(t=>{
                t.Read(a => a.Action("Read", "Grid").Type(HttpVerbs.Post));
                })
            )
        )


    Regards,
    Alexander Popov
    Telerik
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
  8. Siarhei
    Siarhei avatar
    5 posts
    Member since:
    Jan 2016

    Posted 01 Feb Link to this post

    Alexander, thanks for the suggestion, it's great to find out about other techniques Grid supports. For now though I'm going to stick with the initial solution, but will keep these other options in mind should I run into issues.
Back to Top
UI for ASP.NET MVC is VS 2017 Ready