【问题标题】:MVC Dynamic View Binding to Dictionary of Properties for Known and Unknown propertiesMVC 动态视图绑定到已知和未知属性的属性字典
【发布时间】:2019-01-22 19:51:24
【问题描述】:

我一直在尝试为我正在忙于设计的情况找到解决方案,但是我没有设法解决。

想象一下有以下模型

public enum InputType
{
  TextInput,
  LookupInput
}
public struct AdditionalProperty
{
  public string Key {get;set;}
  public string Value {get;set;}
  public InputType Type {get;set;}
}
public class Person
{
  public string FirstName {get;set;}

  public List<AdditionalProperty> AdditionalProperties {get;set;}
}

然后,有以下控制器

public class HomeController
{
  public ActionResult Index()
  {
    var model = new Person { FirstName = "MyName" };
    model.AdditionalProperties = new List<AdditionalProperty>();

    var listItem = new AdditionalProperty
    {
      Key = "Surname",
      Value = "MySurname"
    };
    model.AdditionalProperties.Add(listItem);
    return View(model)
  }
}

我正在寻找的是关于如何“动态”创建具有正确输入类型的属性的 Razor 视图代码,绑定到某些东西,以便我能够在表单被回发时仍然使用模型到控制器的保存功能。

所以已知的属性应该是这样的:

<div class="form-group">
    <div class="form-row">
        <div class="col-md-6">
            @Html.LabelFor(model => model.FirstName, new { @class = "control-label" })
            <div>
                @Html.TextBoxFor(model => model.FirstName, new { @class = "form-control", placeholder = "Enter Group Name", type = "text" })
                @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
            </div>
        </div>
    </div>
</div>

然后的想法是有以下。显然以下是不够的,这就是我需要帮助的地方。 我想根据 property.InputType 显示其他属性,一个在另一个之下,每个在单独的行上(使用引导行)。

@foreach (var property in Model.Properties)
{
  @Html.LabelFor(model => property.Key, new { @class = "control-label" })
  <div>
    @if (property.InputType == TextInput)
    {
      @Html.TextBoxFor(model => property.Value, new { @class = "form-control", placeholder = "Enter Group Name", type = "text" })
    }
    @Html.ValidationMessageFor(model => property.Key, "", new { @class = "text-danger" })
  </div>
}

因此,我希望将我的观点视为:

                 | <label>     | <input>
Known Property   | FirstName   | MyFirstName
Unknown Property | Surname     | MySurname

【问题讨论】:

  • 你不能使用foreach 循环为集合生成表单控件(请参阅Post an HTML Table to ADO.NET DataTable。生成&lt;label&gt; 的代码需要是@Html.LabelFor(m =&gt; AdditionalProperties[i].Key, Model.AdditionalProperties[i].Key, new { ... })。但是那种逻辑应该在自定义 HtmlHelper 方法中转义,而不是视图。此外,您的 ValidationMessageFor() 有点毫无意义,因为您没有验证
  • 斯蒂芬,有没有可能是因为你没有回答我的问题
  • 是的,这是可能的(但使用for 循环或自定义EditorTemplate 以便正确生成表单控件的name 属性 - 请参阅我的第一条评论中的链接)
  • 我知道在处理索引时我应该使用 for。我正在努力解决的概念是为某个项目填充正确的 InputType(文本框、组合框等)
  • 为了使用不同的编辑器模板,我必须根据我提到的类型为每个属性使用不同的模型。

标签: asp.net-mvc razor dynamic view binding


【解决方案1】:

就完整性而言,我发布以下答案。

我将发布模型、视图(索引和编辑器模板)和控制器,以展示我用来测试给出的答案的完整工作解决方案。

我用于此测试的模型类
Person.cs

public class Person
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public List<AdditionalProperty> AdditionalProperties { get; set; }
}

AdditionalProperty.cs

public struct AdditionalProperty
{
    public string Key { get; set; }

    public object Value { get; set; }

    public DateTime? DateValue
    {
        get
        {
            DateTime dateValue;
            if (DateTime.TryParse(Value?.ToString(), out dateValue))
            {
                return dateValue;
            }
            return null;
        }
        set => Value = value;
    }

    public InputType InputType { get; set; }

    public List<SelectListItem> ValueLookupItems { get; set; }
}

我在这里有一个单独的 DateValue 属性的原因是在进行 DateTime 绑定时协助浏览器,否则 DateTimePicker 不会显示。

我使用枚举来确定此特定属性应使用哪种类型的输入类型。
InputType.cs

public enum InputType
{
    TextBox,

    DropdownBox,

    TextArea,

    DateSelection,
}

为了使视图尽可能简单,Stephen 为我提供了 Index View 的示例以及 AdditionalProperty 对象的 EditorTemplate。 EditorTemplate 用于分离关注点,并确保正在使用的输入类型背后的所有逻辑都在一个地方。

我发现 DateTime 属性不能正常工作,所以需要一个额外的 EditorTemplate。我是从 this post 那里得到的。

DateTime.cshtml
注意:模板的位置 -> /Views/Shared/EditorTemplates

@model DateTime
@{
    IDictionary<string, object> htmlAttributes;
    object objAttributes;
    if (ViewData.TryGetValue("htmlAttributes", out objAttributes))
    {
        htmlAttributes = objAttributes as IDictionary<string, object> ?? HtmlHelper.AnonymousObjectToHtmlAttributes(objAttributes);
    }
    else
    {
        htmlAttributes = new RouteValueDictionary();
    }
    htmlAttributes.Add("type", "date");
    String format = (Request.UserAgent != null && Request.UserAgent.Contains("Chrome")) ? "{0:yyyy-MM-dd}" : "{0:d}";
    @Html.TextBox("", Model, format, htmlAttributes)
}

AdditionalProperty.cshtml
注意:模板的位置 -> /Views/Shared/EditorTemplates
注意:AdditionalProperty 的位置是 DynamicViewExample.Models 命名空间的一部分

@model DynamicViewExample.Models.AdditionalProperty
<div>
    @Html.HiddenFor(m => m.Key)
    @Html.LabelFor(m => m.Key, Model.Key, new {@class = "control-label"})
    @if (Model.InputType == DynamicViewExample.Models.InputType.TextBox)
    {
        @Html.TextBoxFor(m => m.Value, new {@class = "form-control"})
    }
    else if (Model.InputType == DynamicViewExample.Models.InputType.TextArea) 
    {
        @Html.TextAreaFor(m => m.Value, new {@class = "form-control"})
    }
    else if (Model.InputType == DynamicViewExample.Models.InputType.DropdownBox)
    {
        @Html.DropDownListFor(m => m.Value, Model.ValueLookupItems, new {@class = "form-control"})
    }
    else if (Model.InputType == DynamicViewExample.Models.InputType.DateSelection)
    {
        @Html.EditorFor(m => m.DateValue, new {@class = "form-control"})
    }
    else
    {
        @Html.HiddenFor(m => m.Value) // we need this just in case
    }
</div

这将是 Index.cshtml 文件的外观

@model DynamicViewExample.Models.Person

@{
    ViewBag.Title = "Home Page";
}

@using (Html.BeginForm())
{
    <div class="row">
        @Html.LabelFor(model => model.FirstName, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.FirstName, new { @class = "form-control", placeholder = "Enter Group Name", type = "text" })
        @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
    </div>
    <div class="row">
        @Html.LabelFor(model => model.LastName, new { @class = "control-label" })
        @Html.TextBoxFor(model => model.LastName, new { @class = "form-control", placeholder = "Enter Group Name", type = "text" })
        @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
    </div>
    <div class="row">
        @Html.EditorFor(m => m.AdditionalProperties, new { htmlAttributes = new { @class = "form-control"}})
    </div>

    <input type="submit" class="btn btn-primary" />
}

最后,HomeController.cs 文件包含一个 Get 和 Post,允许您随意操作数据。这里缺少的是填充模型的“动态”方式,但是一旦将数据库引入混合中,这自然会发生。

    [HttpGet]
    public ActionResult Index()
    {
        var model = new Person
        {
            FirstName = "Gawie",
            LastName = "Schneider",
            AdditionalProperties = new List<AdditionalProperty>
            {
                new AdditionalProperty {Key = "Identification Number", Value = "1234567890123456", InputType = InputType.TextBox},
                new AdditionalProperty {Key = "Date Of Birth", Value = DateTime.Today, InputType = InputType.DateSelection},
                new AdditionalProperty {Key = "Age", Value = "31", InputType = InputType.TextBox},
                new AdditionalProperty {Key = "Gender", Value = "Male", InputType = InputType.DropdownBox,
                    ValueLookupItems = new List<SelectListItem>
                    {
                        new SelectListItem{Text = "Male", Value = "Male"},
                        new SelectListItem{Text = "Female", Value = "Female"}
                    }},
            }
        };

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(Person model)
    {
        //Do some stuff here with the model like writing it to a DB perhaps
        return RedirectToAction("Index");
    }

所以,如果我必须总结一下我在这里尝试做的事情。
我想要实现的目标是能够结合使用强类型/已知属性和动态/未知属性来创建一个系统,该系统允许用户动态创建新输入,而无需开发人员参与。

我真诚地希望有一天这也能对其他人有所帮助。

享受编码体验
高维

【讨论】:

    猜你喜欢
    • 2018-09-07
    • 2017-11-03
    • 2011-06-25
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多