I have a .net core 8 web application, that has a grid that loads data via Ajax. When I edit a row, I'd like the `Contract` column to be a multi-select dropdown. When I edit a row, the control is rendered, but there is no data in it. I have set static data and the data does show up.
Here's my grid:
@(
Html.Kendo().Grid<ContractBenefitViewModel>()
    .Name("ContractBenefitGrid")
    .Columns(columns =>
    {
        columns.Bound(c => c.Id).Hidden(true);
        columns.Bound(c => c.Contracts)
            .ClientTemplate("#= displayContracts(data) #")
            .EditorTemplateName("Contracts")
            .Width(400)
            .Filterable(false)
            .Sortable(false);
        columns.ForeignKey(c => c.BenefitId, (IEnumerable)ViewBag.Benefits, "Id", "DisplayName");
        columns.Bound(c => c.Gl)
            .Width(150);
        columns.Bound(c => c.Comments);
        columns.Bound(c => c.Active).Hidden(true);
        columns.Bound(c => c.Deleted).Hidden(true);
        columns.Command(command =>
            {
                command.Edit();
                command.Destroy();
            })
            .HtmlAttributes(new { style = "text-align: center;" })
            .Width(200);
    })
    .Editable(editable => editable.Mode(GridEditMode.InLine))
    .ToolBar(toolbar =>
    {
        toolbar.Create();
    })
    .Pageable(pager =>
    {
        pager.Refresh(true);
        pager.PageSizes([10, 20, 50]);
    })
    .Sortable()
    .Filterable()
    .NoRecords()
    .DataSource(dataSource => dataSource
        .Ajax()
        .Batch(true)
        .PageSize(10)
        .ServerOperation(true)
        .Events(events => events.Error("error_handler").RequestEnd("onRequestEnd('staticNotifications')"))
        .Model(model =>
        {
            model.Id(p => p.Id);
            model.Field(p => p.Contracts).DefaultValue(ViewBag.ContractId as List<ContractModel> ?? new List<ContractModel>());
            model.Field(p => p.BenefitValue).DefaultValue(ViewBag.BenefitId);
            model.Field(p => p.Comments);
            model.Field(p => p.LastUpdatedBy).DefaultValue(ViewData[Literals.EmailKey]);
            model.Field(p => p.Active).Editable(false).DefaultValue(Literals.Yes);
            model.Field(p => p.Deleted).Editable(false).DefaultValue(Literals.No);
        })
        .Filter(f =>
        {
            f.Add(x => x.Active).IsEqualTo(Literals.Yes);
            f.Add(x => x.Deleted).IsEqualTo(Literals.No);
        })
        .Read(read => read.Action("GetContractBenefits", "ContractBenefit").Data("forgeryToken"))
        .Create(create => create.Action("CreateContractBenefits", "ContractBenefit").Data("forgeryToken"))
        .Update(update => update.Action("UpdateContractBenefits", "ContractBenefit").Data("forgeryToken"))
        .Destroy(destroy => destroy.Action("DeleteContractBenefits", "ContractBenefit").Data("forgeryToken"))
        .Sort(sort =>
        {
            sort.Add(x => x.BenefitValue);
        })
    )
)Here's the JavaScript
<script type="text/javascript">
    function displayContracts(data) {
        window.console.log('displayContracts()');
        return $.map(data.Contracts, function (e) { return e.DisplayName; }).join(", ");
    }
</script>Here's my model
public class ContractBenefitViewModel
{
    public long Id { get; set; }
    public long BenefitId { get; set; }
    [Display(Name = "Benefit")]
    public string BenefitValue
    {
        get
        {
            var sb = new StringBuilder();
            if (Benefit is not null)
            {
                sb.Append(Benefit.Value);
                if (!string.IsNullOrWhiteSpace(Benefit.Gl))
                {
                    sb.Append(" (");
                    sb.Append(Benefit.Gl);
                    sb.Append(")");
                }
            }
            return sb.ToString();
        }
    }
    [Display(Name = "GL Code")]
    public string Gl => Benefit?.Gl ?? string.Empty;
    [Display(Name = "Comments")]
    [StringLength(ModelLiterals.CommentsLength)]
    [MaxLength(ModelLiterals.CommentsLength)]
    public string? Comments { get; set; }
    public string Active { get; set; } = ModelLiterals.Yes;
    public string Deleted { get; set; } = ModelLiterals.No;
    public string LastUpdatedBy { get; set; } = string.Empty;
    public BenefitModel? Benefit { get; set; } = null!;
    [UIHint("Contracts")]
    public IList<ContractModel> Contracts { get; set; } = new List<ContractModel>();
}Here's my Editor Template (named Contracts.cshtml, in the EditorTemplates folder)
@model Lookups.ContractModel
<div style="width: 99%">
    @(Html.Kendo().MultiSelectFor(m => m)
    .DataValueField("Id")
    .DataTextField("DisplayName")
    .DownArrow()
    .AutoClose(false)
    .BindTo((IEnumerable<Lookups.ContractModel>)ViewData["contracts"])
        )
</div>Here's the Index and Get data methods in my controller
[HttpGet]
public async Task<IActionResult> IndexAsync()
{
    Logger.LogDebug("{NameOf}()", nameof(IndexAsync));
    var contracts = await Client.GetContractsAsync();  //Client is my API client library - it fetches data from my API
    var benefits = await Client.GetBenefitsAsync();
    ViewBag.Contracts = contracts;
    ViewData["contracts"] = contracts;
    ViewBag.Benefits = benefitFinancialSupports;
    ViewBag.DefaultContractId = contracts?.FirstOrDefault()?.Id ?? -1;
    ViewBag.DefaultBenefitId = benefits?.FirstOrDefault()?.Id ?? -1;
    return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<JsonResult> GetContractBenefitsAsync([DataSourceRequest] DataSourceRequest request)
{
    Logger.LogDebug("{NameOf}()", nameof(GetContractBenefitsAsync));
    var stopwatch = Stopwatch.StartNew();
    try
    {
        if (!ModelState.IsValid) return Json(InvalidModelStateMessage);
        var models = await Client.GetContractBenefitsAsync();
        var viewModels = models
            .Where(x => x is { Benefit: not null, Contract: not null })
            .GroupBy(x => x.BenefitId)
            .Select(g => new ContractBenefitViewModel
            {
                BenefitId = g.Key,
                Benefit = g.First().Benefit,
                Contracts = g.Select(x => x.Contract!).Distinct().ToList(),
                Id = g.First().Id,
                Comments = g.First().Comments,
                Active = g.First().Active,
                Deleted = g.First().Deleted,
                LastUpdatedBy = g.First().LastUpdatedBy
            })
            .ToList();
        return Json(await viewModels.ToDataSourceResultAsync(request));
    }
    catch (Exception ex)
    {
        Logger.LogError(ex, "{NameOf}()", nameof(GetContractBenefitsAsync));
        return Json(new { success = false, message = ex.Message });
    }
    finally
    {
        stopwatch.Stop();
        Logger.LogInformation("**** {NameOf} took [{Elapsed}]", nameof(GetContractBenefitsAsync),
            stopwatch.Elapsed);
    }
}
I should mention that I store the data one ContractId with one BenefitId (in a Mapping table), so when I return data via the `Client.GetContractBenefitsAsync()` call, it returns a List of 1-to-1 records, and I group them so I get a single Benefit with all Contracts that are associated with it (hence the Linq GroupBy call to build my ViewModel). This is how I want to present it to my users, so they can select as many Contracts as they want in the UI, and behind the scenes I'll turn into a 1:1 association. I'm just having trouble getting my data (the Contracts Multi Select) to display data. Am I missing something or is there another way to do this?

