Grid - trouble binding a custom popup editor with tag helpers in razor page

2 Answers 301 Views
Grid
T
Top achievements
Rank 1
Iron
T asked on 19 Sep 2022, 04:20 PM | edited on 19 Sep 2022, 04:57 PM

I'm having trouble getting a custom popup editor bound to a model using tag helpers, razor pages, and entity framework.

Index.cshtml.cs

using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using MyApp.Data.Models;
using MyApp.Interfaces;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace MyApp.Pages
{
    public class IndexModel : PageModel
    {
        private readonly IDataService _dataService = null!;

        public List<Item> Items { get; set; }

        public IndexModel(IDataService dataService)
        {
            _dataService = dataService;
            Items = new();
        }

        public async Task<JsonResult> OnPostCreate([DataSourceRequest] DataSourceRequest request, Item item)
        {
            await _dataService.CreateItemAsync(item);

            return new JsonResult(new[] { item }.ToDataSourceResult(request, ModelState));
        }

        public async Task<JsonResult> OnPostRead([DataSourceRequest] DataSourceRequest request, bool isFound)
        {
            Items = await _dataService.GetActiveItemsAsync(isFound);
            var result = await Items.ToDataSourceResultAsync(request);

            return new JsonResult(result);
        }

        public async Task<JsonResult> OnPostUpdate([DataSourceRequest] DataSourceRequest request, Item item)
        {
            var updatedItem = await _dataService.UpdateItemAsync(item);

            return new JsonResult(new[] { updatedItem }.ToDataSourceResult(request, ModelState));
        }

        public async Task<JsonResult> OnPostDestroy([DataSourceRequest] DataSourceRequest request, Item item)
        {
            await _dataService.DeleteItem(item.Id);

            return new JsonResult(new[] { item }.ToDataSourceResult(request, ModelState));
        }
    }
}

Index.cshtml

@page
@model IndexModel
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@Html.AntiForgeryToken()

<kendo-grid name="grid" height="700">
    <filterable enabled="true" />
    <groupable enabled="true" />
    <sortable enabled="true" />
    <editable enabled="true" mode="popup" template-id="popup-editor" />
    <scrollable enabled="true" />
    <pageable enabled="true" page-sizes="new int[] { 10, 20, 50}" />
    <toolbar>
        <toolbar-button template="toolbarTemplate"></toolbar-button>
        <toolbar-button name="create"></toolbar-button>
    </toolbar>
    <columns>
        <foreign-key-column field="ItemTypeId" />
        <column field="ReportDate" title="Date Reported" format="{0:yyyy-MM-dd}" width="160" />
        <column field="ItemDescription" title="Description" />
        <column field="Unit" title="Unit" width="200" />
        <column field="ContactName" title="Contact Name" width="250" />
        <column field="ReceivedBy" title="Received By" hidden="true" />
        <column field="ContactPhone" title="Contact Phone" hidden="true" />
        <column field="ContactAddress" title="Contact Address" hidden="true" />
        <column field="ContactDetails" title="Contact Details" hidden="true" />
        <column field="Qty" title="Item Quantity" hidden="true" format="{0:0}" />

        <column width="180">
            <commands>
                <column-command name="edit"></column-command>
                <column-command name="destroy"></column-command>
            </commands>
        </column>
    </columns>
    <datasource type="DataSourceTagHelperType.Ajax" page-size="10">
        <transport>
            <read url="/Index?handler=Read" data="getDataParameters" />
            <create url="/Index?handler=Create" data="getDataParameters" />
            <update url="/Index?handler=Update" data="getDataParameters" />
            <destroy url="/Index?handler=Destroy" data="getDataParameters" />
        </transport>
        <sorts>
            <sort field="ReportDate" direction="desc" />
        </sorts>
        <schema data="Data" total="Total" errors="Errors">
            <model id="Id">
                <fields>
                    <field name="Id" type="number" editable="false" />
                    <field name="ItemTypeId" type="object" />
                    <field name="ReportDate" type="date" />
                    <field name="ItemDescription" type="string" />
                    <field name="Unit" type="string" />
                    <field name="ContactName" type="string" />
                    <field name="ReceivedBy" type="string" />
                    <field name="ContactPhone" type="string" />
                    <field name="ContactAddress" type="string" />
                    <field name="ContactDetails" type="string" />
                    <field name="Qty" type="number" default-value="1" />
                </fields>
            </model>
        </schema>
    </datasource>
</kendo-grid>

<script id="popup-editor" type="text/x-kendo-template">
    @await Html.PartialAsync("_ItemPartial")
</script>

_ItemPartial.cshtml

<!--

the following @model throws:

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'MyApp.Pages.IndexModel', but this ViewDataDictionary instance requires a model item of type 'MyApp.Data.Models.Item'.

-->

@model MyApp.Data.Models.Item

 

<!-- the following @model shows an empty grid: -->

@model IndexModel

<h6>magical mystery template</h6>

The questions I have are:

  • Which model should be used in _ItemPartial.cshtml?
  • Is the code in Index.cshtml.cs correctly using the Item property?
  • Within the popup dialog, how should the Update button be wired?

2 Answers, 1 is accepted

Sort by
1
Accepted
T
Top achievements
Rank 1
Iron
answered on 26 Sep 2022, 06:59 PM

After a silly number of hours we decided to roll our own grid for this and future projects. For others stuck on this, the following is the only thing that worked for us:

Editable tag:

<editable enabled="true" mode="popup" template-id="popup-editor" />

Instead of partial file, using chrome dev tools, we scraped html from the baked-in popup editor and tweaked it for the data binding, then put it in the same "Index.cshtml" file within a script tag:

<script type="text/html" id="popup-editor">
    <div class="k-edit-form-container k-widget k-form">
        <div class="k-form-field">
            <label class="k-label k-form-label" for="ReportDate">Date Reported</label>
            <input type="text" id="ReportDate" name="ReportDate" title="Date Reported" data-type="date" data-bind="value:ReportDate" data-format="yyyy-MM-dd" data-role="datepicker" role="combobox" autocomplete="off">
        </div>
        <div class="k-form-field">
            <label class="k-label k-form-label" for="ItemDescription">Description</label>
            <input type="text" id="ItemDescription" name="ItemDescription" title="Description" data-bind="value:ItemDescription" data-role="textbox" aria-disabled="false" autocomplete="off">
        </div>
...
Stoyan
Telerik team
commented on 27 Sep 2022, 12:35 PM

Hi T,

I am glad the experienced issue has been resolved.

You have found out the correct solution as I have missed mentioning that the Grid's Editable templates behave differently when TagHelpers are used. My previous suggestion is valid for HtmlHelpers (when Razor pages are used the Editor Templates are located in the Pages/Shared/EditorTemplates directory).

However with TagHelpers the Editable templates should be defined locally in script tags.

Additionally, you can configure Telerik Components in the templates by setting their is-in-client-template="true":

<script id="popup-editor" type="text/html">
        <kendo-textbox is-in-client-template="true" name="ItemDescription" placeholder="Add description">
        </kendo-textbox>
</script>

Hopefully this information is useful.

0
Stoyan
Telerik team
answered on 22 Sep 2022, 12:57 PM

Hello T,

The issue arises because the template-id configuration of the Grid's editors isn't referring to a regular Kendo Template on the page but to a View that should be located at the Views/Shared/EditorTemplates directory.

This View uses the Model of the Grid's indirectly. Instead it utilizes the properties of the Model and their type to provide a skeleton of the anticipated data. Then the data of the edited row is bound to the PopUp window's Components on the client-side.

To ensure that the data binds properly - the names of the Editors of the Template should correspond to the Model's property names.

For example to create a TextBox for the ItemDescription field enter the following code:

<kendo-textbox name="ItemDescription">
            <textbox-label content="Description"/>
</kendo-textbox>

Finally, the Update and Cancel buttons are created by the Component internally.

I hope this information is insightful.

Please let me know, if more questions arise.

Regards,
Stoyan
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

T
Top achievements
Rank 1
Iron
commented on 22 Sep 2022, 05:45 PM | edited

Thanks for the info on the popup field binding. That will help once the popup is rendered.

I looked over the template documentation which suggests using <script> blocks to render external templates. But you're saying that the "template-id" attribute references a partial view located elsewhere to render the external template?

The documentation uses Html Helper syntax but this project is using tag helpers.  Also, this project uses Razor pages so there is no "View" folder. So I created a "Views/Shared/EditorTemplates" folder and put the following _PopupEditor.cshtml file there, and also in the "Pages" folder and also in a "Pages/Shared" folder. But the following tags did not work:

<editable enabled="true" mode="popup" template-id="_PopupEditor"/>
<editable enabled="true" mode="popup" template-id="_PopupEditor.cshtml"/>
<editable enabled="true" mode="popup" template-id="/Shared/_PopupEditor"/>
<editable enabled="true" mode="popup" template-id="/Shared/_PopupEditor.cshtml"/>
<editable enabled="true" mode="popup" template-id="/Shared/EditorTemplates/_PopupEditor"/>
<editable enabled="true" mode="popup" template-id="/Shared/EditorTemplates/_PopupEditor.cshtml"/>

_PopupEditor.cshtml

<h6>This is a template</h6>

Followup questions:

  • in what folder should "_PopupEditor.cshtml" live?
  • how should the <editable> tag helper be formed?
Tags
Grid
Asked by
T
Top achievements
Rank 1
Iron
Answers by
T
Top achievements
Rank 1
Iron
Stoyan
Telerik team
Share this question
or