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

Razorpage Grid with Checkboxes Example result - hope this helps others

5 Answers 802 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Al
Top achievements
Rank 1
Al asked on 04 Jan 2020, 08:08 PM

I just finished a rather painful journey into putting together a demo to be able to use the Telerik Grid on a Razor page at all and then how to add checkboxes to it. Sounds easy, but it was not. Just try to find useful (or in some cases any) documentation on this stuff. 

Start with the https://github.com/telerik/ui-for-aspnet-core-examples solution and use the Kendo.Examples.RazorPages project. I added a Mange folder under pages and a "Lab" page under that (Lab.cshtml / Lab.cshtml.cs) - here is the code and my discussion follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
 
namespace Kendo.Examples.RazorPages.Pages.Manage
{
    public class LabModel : PageModel
    {
        public static IList<ClientDto2> ClientsDtos;
 
        [TempData]
        public string TempMessage { get; set; }
        public bool ShowTempMessage => !string.IsNullOrWhiteSpace(TempMessage);
 
        public void OnGet()
        {
            Console.WriteLine("Manage OnGet");
            try
            {
                if (ClientsDtos != null) return;
 
                ClientsDtos = new List<ClientDto2>();
                //var json = await GetClientJson().ConfigureAwait(false);
                // substitute test data
                var json = "[{\"id\": 1,\"clientId\": \"client1\",\"clientName\": \"Client Credentials 1\",\"description\": \"Credentials 1\",\"userCodeType\": \"1\"}," +
                           "{\"id\": 2,\"clientId\": \"client2\",\"clientName\": \"Client Credentials 2\",\"description\": \"Credentials 2\",\"userCodeType\": \"2\"}]";
                var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
                var clientList = JsonSerializer.Deserialize<List<ClientDto2>>(json, options);
 
                foreach (var client in clientList)
                {
                    ClientsDtos.Add(new ClientDto2()
                    {
                        Id = client.Id,
                        ClientId = client.ClientId ?? "0",
                        ClientName = client.ClientName ?? "None",
                        Description = client.Description ?? "None",
                        UserCodeType = client.UserCodeType ?? "None",
                        Admin = client.Id % 2 == 1
                    });
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                TempMessage = ex.Message;
                if (ex.InnerException != null)
                    TempMessage += " : " + ex.InnerException.Message;
            }
        }
 
        public JsonResult OnPostRead([DataSourceRequest] DataSourceRequest request)
        {
            var res = new JsonResult(ClientsDtos.ToDataSourceResult(request));
            return res;
        }
 
        public JsonResult OnPostUpdate([DataSourceRequest] DataSourceRequest request, ClientDto2 client)
        {
            // the updatedClientData contains the updated data (only one record at a time in this example)
            var updatedClientData = ClientsDtos.Where(x => x.Id == client.Id).Select(x => client);
            // test changing data here
            client.UserCodeType = "99";
            TempMessage =
                $"Admin = {client.Admin}, Management = {client.Management}, Office = {client.Office} ";
            // use this data to update DB here and return the updated data
            return new JsonResult(new[] { client }.ToDataSourceResult(request, ModelState));
        }
    }
 
    public class ClientDto2
    {
        public int Id { get; set; }
        public string ClientId { get; set; }
        public string ClientName { get; set; }
        public string Description { get; set; }
        public string UserCodeType { get; set; }
        public bool Admin { get; set; }
        public bool Management { get; set; }
        public bool Office { get; set; }
    }
}

 

@page
@model Kendo.Examples.RazorPages.Pages.Manage.LabModel
@using Kendo.Mvc.UI
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@Html.AntiForgeryToken()
 
@{
    ViewData["Title"] = "Lab";
}
 
<h1>Lab</h1>
<form method="post">
    @*FYI the following does not fire unless the page is reloaded
    it does not fire for a data update*@
    @if (!string.IsNullOrEmpty(Model.TempMessage))
    {
        var statusMessageClass = Model.TempMessage.StartsWith("Error") ? "danger" : "success";
        @if (Model.TempMessage != "")
        {
            <div class="alert alert-@statusMessageClass alert-dismissible" role="alert">
                <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
                @Model.TempMessage
            </div>
        }
    }
 
    @(Html.Kendo().Grid<ClientDto2>().Name("grid")
        .Columns(columns =>
        {
            columns.Bound(column => column.ClientId).Width(100).Title("Client");
            columns.Bound(column => column.ClientName);
            columns.Bound(column => column.Description);
            columns.Bound(column => column.UserCodeType);
            columns.Bound(column => column.Admin)
                .ClientTemplate(
                    "<input class='k-checkbox' data-val=#: Admin# id='Admin#: id#' name='Admin' " +
                    "type='checkbox' #= Admin ? checked='checked' : '' # onclick='return false;' value=#: Admin# >" +
                    "<label for='Admin#: id#' class='k-checkbox-label'></label>");
            columns.Bound(column => column.Management)
                .ClientTemplate(
                    "<input class='k-checkbox' data-val=#: Management# id='Management#: id#' name='Management' " +
                    "type='checkbox' #= Management ? checked='checked' : '' # onclick='return false;' value=#: Management# >" +
                    "<label for='Management#: id#' class='k-checkbox-label'></label>");
            columns.Bound(column => column.Office)
                .ClientTemplate(
                    "<input class='k-checkbox' data-val=#: Office# id='Office#: id#' name='Office' " +
                    "type='checkbox' #= Office ? checked='checked' : '' # onclick='return false;' value=#: Office# >" +
                    "<label for='Office#: id#' class='k-checkbox-label'></label>");
            columns.Command(column => { column.Edit(); });
        })
        .DataSource(ds => ds.Ajax()
            .Read(r => r.Url("/Manage/Lab?handler=Read").Data("forgeryToken"))
            .Update(u => u.Url("/Manage/Lab?handler=Update").Data("forgeryToken"))
            .Create(c => c.Url("/Manage/Lab?handler=Create").Data("forgeryToken"))
            .Destroy(d => d.Url("/Manage/Lab?handler=Destroy").Data("forgeryToken"))
            .Model(m => m.Id(id => id.Id))
            .PageSize(20)
        )
        .Scrollable()
        )
</form>
 
<script type="text/javascript">
    function forgeryToken() {
        return kendo.antiForgeryTokens();
    }
</script>

 

By far the biggest hurdle here was adding in the checkboxes. The default grid shows "true" and "false" until you go onto edit mode and then it shows an actual checkbox (BTW that comes from Shared/EditorTemplates/Boolean.cshtml). It sure would be nice if the grid default would be to show the checkboxes but since that is not the case I went in search of how to show them. At first I went down the rabbit hole at the https://demos.telerik.com/aspnet-core/grid/rowtemplate page - I could not get this to work. In my case it seems if you add ClientRowTemplate that wipes out any other column data and I could not get it to work like the display shows after many tries. Then I stumbled on the ClientTemplate (good luck finding any documentation on this one!) and after much trial and error I got a checkbox on the grid, but without a class and it was ugly and out of place so added the "k-checkbox" class and the checkbox disappeared. It turns out you need to add the label next to it or it disappears - WTF! Lastly I needed to make the checkbox read only since clicking it in browse mode would change it but that would not change the underlying data and so I found an HTML hack to add the onclick return false function which essentially makes it read only. - So holy crap this was a painful journey but I hope this post helps others. 

5 Answers, 1 is accepted

Sort by
0
Tsvetomir
Telerik team
answered on 08 Jan 2020, 11:45 AM

Hi Al,

Thank you for sharing the approach that you have undertaken along with the valuable clarifications. There are a few points worth mentioning. 

The RowTemplate option of the Kendo UI Grid completely overrides the row implementation. It gives the freedom the construct the row's content according to one's preferences. You could include multiple field values within one cell, for instance.

The ClientTemplate option is column-based and it per-column. It changes the appearance of the cell. You could set it to anything that fits your requirements. Since the client template is not an editor template, having editors such as the checkbox editors might be misleading for the users. This is due to the fact that this template is designed to display values only and not to edit the underlying field. 

The recommended way to have an editor in any of the grid's columns is to attach the click/change event to the editor. Within the handler, access the underlying data item and change the value of the field. It is important to point out that the cell should be made non-editable in order to prevent the grid from rendering its default editor. It could be done by setting the .Editable() option of the column and returning a false value:

.Editable("notEditable")

function notEditable(e){
     return false;
}

Until very recently the built input with type checkbox was completely non-customizable. Simple CSS did not have any effect on the customization of the appearance. That is why the Kendo UI team created an input that is hidden and a label that was customizable. Therefore, styling the label while using the checkbox functionality of the input. 

I hope that you find those clarifications helpful. Let me know in case any further information is needed.

 

Regards,
Tsvetomir
Progress Telerik

Get quickly onboarded and successful with Telerik UI for ASP.NET Core with the dedicated Virtual Classroom technical training, available to all active customers.
0
Al
Top achievements
Rank 1
answered on 08 Jan 2020, 04:05 PM

Thank you for your response and clarification. Now that you explained the RowTemplate option I'm confused as to the example at https://demos.telerik.com/aspnet-core/grid/rowtemplate - what is the purpose of the lines after columns.Template? If they are replaced by the row template then are they not needed? If they are not needed they sure confuse the example. If they are needed then their purpose should be explained - TIA 

.Columns(columns =>
   {
       columns.Template(" ").Width(140).Title("Picture");
       columns.Bound(e => e.Title).Width(400).Title("Details");
       columns.Bound(e => e.Country);
       columns.Bound(e=> e.EmployeeID).Title("ID");
   })
0
Tsvetomir
Telerik team
answered on 09 Jan 2020, 03:26 PM

Hi Al,

The lines after the template are there because an actual table is rendered via the template. If you take a look at the template that is provided, you would notice that there are "td" elements for each section. Adding multiple cell elements makes the content of the grid a table. If you do not want to show the lines, you should declare only one cell and format its content accordingly. 

As per the code snippet that you have provided, is there an issue that you have stumbled upon and has to be resolved? 

 

Regards,
Tsvetomir
Progress Telerik

Get quickly onboarded and successful with Telerik UI for ASP.NET Core with the dedicated Virtual Classroom technical training, available to all active customers.
0
Al
Top achievements
Rank 1
answered on 09 Jan 2020, 03:37 PM
No issue at the moment. Just trying to gain an understanding. So as I understand your explanation the template fills in the data of the grid and the items in the columns section fill in the header titles and set the column widths and act as a placeholder for the template defined data - correct?
0
Tsvetomir
Telerik team
answered on 13 Jan 2020, 12:10 PM

Hi Al,

The rowTemplate option of the grid gives the developer the chance to create an appearance according to their preferences. Rather than providing placeholders, the grid provides the data item of the corresponding row. Therefore, via templates, you could access all of the field values of the data item and place them wherever you like. 

The header row, however, in the ASP.NET Core environment will be taken from the columns declaration of the grid. If you would like to create only one column, you could pass an empty template:

columns.Template(e => { }).ClientTemplate(" ").Width(140).Title("Picture");

However, this would not be an efficient approach as you could bind it to a single field and specify a client template rather than going with the approach of the RowTemplate.

 

Regards,
Tsvetomir
Progress Telerik

Get quickly onboarded and successful with Telerik UI for ASP.NET Core with the dedicated Virtual Classroom technical training, available to all active customers.
Tags
Grid
Asked by
Al
Top achievements
Rank 1
Answers by
Tsvetomir
Telerik team
Al
Top achievements
Rank 1
Share this question
or