0

I see this answer from 7 years ago. Just wondering if there have been any changes or work arounds to get this to work.

Problem

We are trying to "extend" a Kendo DropDownList editor template by creating specific editor templates that set a minimum set of unique attributes, then passes that info to the base Kendo DropDownList editor template to help keep things DRY. (I mean, that is a core principle in development so to see the tools not allow it is a bit puzzling.)

But it seems the model type details are lost when calling the Base editor via Html.Partial. And using a truly nested Html.EditorFor within an editor template doesn't work either.

For example:

The model is marked as int?, but if the model value is populated, the html markup is rendered as Required. If the model value is null, it is not.

Further, Validation Messages are genericized to the primitive type so instead of an error message like: "Account Id is required." you get "The Int32 field is required."

Model:

[Display(Name = "Expense Account")]
[UIHint("FinancialsChartOfAccounts-ExpenseEditor")]
public int? FinancialsPurchaseExpenseAccountId { get; set; }

"Derived" Editor

@{
    var editorOptionsModel = new OTIS.Domain.DDLEditorOptionsModel();
    editorOptionsModel.AreaName = "Shared";
    editorOptionsModel.ControllerName = "ManageDDLs";
    editorOptionsModel.ControllerAction = "GetChartOfAccountsExpense";
    editorOptionsModel.Width = ViewData["WidthPixels"] != null ? (string)ViewData["WidthPixels"] : null;
    editorOptionsModel.DefaultLabelText = ViewData["DefaultText"] != null ? (string)ViewData["DefaultText"] : "Select One...";
    
    ViewData["ddlEditorOptionsModel"] = editorOptionsModel;
}
@Html.Partial("~/Views/_Base/Shared/EditorTemplates/DDLEditor.cshtml", (int?)Model)

"Base" Editor:

@using Kendo.Mvc.UI;
@using Kendo.Mvc.UI.Fluent;
@{              
    //set default option values
    string strAreaName = "Shared";
    string strControllerName = "ManageDDLs";
    string strOptionLabel = "Select One...";
    string strControllerAction = (string)ViewData["ControllerAction"];
    string widthPixels = "100%";

    //check to see if any options are passed in via view data from the view
    if (ViewData["WidthPixels"] != null)
    {
        widthPixels = (string)(ViewData["WidthPixels"].ToString());
    }

    //see if a option model is passed in, and if so, use those values
    var ddlEditorOptionsModel = new OTIS.Domain.DDLEditorOptionsModel();

    if (ViewData["ddlEditorOptionsModel"] != null)
    {
        ddlEditorOptionsModel = (OTIS.Domain.DDLEditorOptionsModel)ViewData["ddlEditorOptionsModel"];
        strAreaName = ddlEditorOptionsModel.AreaName ?? strAreaName;
        strControllerName = ddlEditorOptionsModel.ControllerName ?? strControllerName;
        strControllerAction = ddlEditorOptionsModel.ControllerAction ?? strControllerAction;
        widthPixels = ddlEditorOptionsModel.Width ?? widthPixels;
        strOptionLabel = ddlEditorOptionsModel.DefaultLabelText ?? strOptionLabel;
    }

    IDictionary<string, object> htmlAttributes = new RouteValueDictionary();
    htmlAttributes["style"] = "width:" + widthPixels + ";min-width:250px;max-width:500px;";

    if (ViewData["HtmlAttributes"] != null)
    {
        htmlAttributes = Html.MergeHtmlAttributes(ViewData["HtmlAttributes"], htmlAttributes);
    }

    DropDownListBuilder builder;

    builder = Html.Kendo().DropDownListFor(model => model)
        .HtmlAttributes(htmlAttributes)
        .DataValueField("Value")
        .DataTextField("DisplayText")
        .OptionLabel(strOptionLabel)
        .Template("#= data.DisplayText.replace(' ', '&nbsp;') #");

    //set data source
    if (ddlEditorOptionsModel.Data == null)
    {
        builder.DataSource(source =>
        {
            source.Read(read =>
            {
                read.Action(strControllerAction, strControllerName, new { area = strAreaName });
            })
            .ServerFiltering(true);
        });
    }
    else
    {
        builder.BindTo(ddlEditorOptionsModel.Data);
    }

    if (ddlEditorOptionsModel.Height != null)
    {
        builder.Height(ddlEditorOptionsModel.Height.Value);
    }
}
@(builder)
    
@Html.ValidationMessageFor(model => model)
crichavin
  • 4,672
  • 10
  • 50
  • 95

1 Answers1

0

That seems like a lot of logic in the Editor templates/partials/views to me...

Why not move the logic out and into models?:

//Interface

public interface IKendoDropDownListModel
{

    string AreaName { get; set; }

    string ControllerName { get; set; }

    string ActionName { get; set; }

    string ClientTemplate { get; set; }

    string OptionLabel { get; set; }

    string DisplayText { get; set; }

    string Value { get; set; }

    int Width { get; set; }

}


//Base Editor Model

public class KendoDropDownListModel : IKendoDropDownListModel
{
    public KendoDropDownListModel(string areaName, string controllerName, string actionName)
    {
        AreaName = areaName;
        ControllerName = controllerName;
        ActionName = actionName;
        OptionLabel = "please select...";
        Width = 100;
    }

    public string AreaName { get; set; }

    public string ControllerName { get; set; }

    public string ActionName { get; set; }

    public string ClientTemplate { get; set; }

    public string OptionLabel { get; set; }

    public string DisplayText { get; set; }

    public string Value { get; set; }

    public int Width { get; set; }

}

//Inheriting Model

public class FinancialsChartOfAccountsDropdown : KendoDropDownListModel
{
    public FinancialsChartOfAccountsDropdown() : base("area", "controller", "action")
    {
        Width = 400;
    }

}

//Input/View Model example

public class ModelUsingDropdown
{
    public ModelUsingDropdown()
    {
        Dropdown = new FinancialsChartOfAccountsDropdown();
    }

    [UIHint("KendoDropDownListModel")]
    public FinancialsChartOfAccountsDropdown Dropdown { get; set; }

}

//Single Editor Template

@model IKendoDropDownListModel

@(Html.Kendo().DropDownListFor(m => m)
          .OptionLabel(Model.OptionLabel)
          .DataTextField("DisplayText")
          .DataValueField("Value")
            .DataSource(source => source
                      .Transport(transport => transport
                        .Read(read =>
                        {
                            read.Action(Model.Action, Model.Controller);
                        }))
                )
            .Width(Model.Width)
          )

Then when you post the model you just have to get the value from the object

    [HttpPost]
    public ActionResult SomeAction(ModelUsingDropdown model)
    {
        string valueFromDropdown = model.Dropdown.Value;

        return View();
    }
Jon Ryan
  • 1,497
  • 1
  • 13
  • 29