自定义 CheckBoxListFor Html 助手。如何绑定到视图模型 属性?
Custom CheckBoxListFor Html helper. How do I bind to a viewmodel property?
我正在创建一个自定义 Html 帮助程序来处理复选框列表。我知道网上有大量关于这个主题的信息,但我还没有看到太多关于尝试以下面的方式生成 HTML 的信息。
需要渲染HTML
<ul>
<li>
<label>
<input type="checkbox" name="Genres" value="SF" />
Science Fiction
</label>
</li>
<li>
<label>
<input type="checkbox" name="Genres" value="HR" />
Horror
</label>
</li>
<!-- more genres -->
</ul>
我使用不同的复选框 值 而不仅仅是布尔值,因为我想利用这样一个事实,即在表单 post 上,我会得到一个逗号- 选定 值的分隔列表。例如,如果同时选择科幻和恐怖,我会得到 "SF,HR"
.
查看模型
public class MovieViewModel
{
public string Name { get;set; }
public string Year { get;set; }
public IEnumerable<string> Genres { get;set; }
public IEnumerable<SelectListItem> GenreOptions { get;set; }
}
查看
在我看来,我想基本上这样做:
@Html.EditorFor(model => model.Name)
@Html.EditorFor(model => model.Year)
@Html.CheckBoxListFor(model => model.Genres, Model.GenreOptions)
自定义 Html 助手
所以,我开始创建一个自定义的 Html 助手,但我在这些方面没有太多经验,这就是我需要帮助的地方:
public static MvcHtmlString CheckboxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> items)
{
if (items == null) return null;
var itemsList = items.ToList();
if (!itemsList.Any()) return null;
// How do I get "Genres" name from the passed expression from viewmodel, without passing the hardcoded string "genres"?
var checkboxGroupName = expression.what????
var sb = new StringBuilder();
sb.Append("<ul>");
foreach (var item in itemsList)
{
var checkbox = $"<input type=\"checkbox\" name=\"{checkboxGroupName}\" value=\"{item.Value}\" />";
sb.Append($"<li class=\"checkbox\"><label>{checkbox} {HttpUtility.HtmlEncode(item.Text)}</label></li>");
}
sb.Append("</ul>");
return MvcHtmlString.Create(sb.ToString());
}
正如您在上面的评论中看到的,我不知道如何将视图模型 属性 名称 "Genres" 动态分配给复选框的 name
属性,而不对其进行硬编码.我如何从 model => model.Genres
表达式中获取它?
您可以从使用 ModelMetaData class 使用以下代码行:
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var checkboxGroupName = metadata.PropertyName;
这是一篇解释How to Create Custom Strongly Typed Html Helpers的文章,可能对您有更多帮助。
要获取 属性 名称,请使用
var name = ExpressionHelper.GetExpressionText(expression);
这将给出完全限定的名称,例如 @CheckboxListFor(m => m.Movies.Genres, ....)
将 return Movies.Genres
.
但是要考虑到您为模型使用自定义 EditorTemplate
的情况(这是父模型的 属性),那么您还需要获取 `HtmlFieldPrefix 并且如果它存在,在其前面加上名称。
您当前的代码没有为您提供正确的绑定,例如,如果 Genres
包含与 SelectListItem
的 Value
属性 相匹配的值,则关联的复选框应该是渲染视图时检查。你的代码应该是
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> items)
{
if (items == null)
{
throw new ArgumentException("...");
}
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var model = metadata.Model as IEnumerable<string>;
if (model == null)
{
throw new ArgumentException("...");
}
// Get the property name
var name = ExpressionHelper.GetExpressionText(expression);
// Get the prefix in case using a EditorTemplate for a nested property
string prefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix;
if (!String.IsNullOrEmpty(prefix))
{
name = String.Format("{0}.{1}", prefix, name);
}
StringBuilder html = new StringBuilder();
foreach (var item in items)
{
StringBuilder innerHtml = new StringBuilder();
TagBuilder checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("name", name);
checkbox.MergeAttribute("value", item.Value);
if (model.Contains(item.Value))
{
checkbox.MergeAttribute("checked", "checked");
}
innerHtml.Append(checkbox.ToString());
TagBuilder text = new TagBuilder("span");
text.InnerHtml = item.Text;
innerHtml.Append(text.ToString());
TagBuilder label = new TagBuilder("label");
label.InnerHtml = innerHtml.ToString();
TagBuilder li = new TagBuilder("li");
li.AddCssClass("checkbox");
li.InnerHtml = label.ToString();
html.Append(li);
}
TagBuilder ul = new TagBuilder("ul");
ul.InnerHtml = html.ToString();
return MvcHtmlString.Create(ul.ToString());
}
我正在创建一个自定义 Html 帮助程序来处理复选框列表。我知道网上有大量关于这个主题的信息,但我还没有看到太多关于尝试以下面的方式生成 HTML 的信息。
需要渲染HTML
<ul>
<li>
<label>
<input type="checkbox" name="Genres" value="SF" />
Science Fiction
</label>
</li>
<li>
<label>
<input type="checkbox" name="Genres" value="HR" />
Horror
</label>
</li>
<!-- more genres -->
</ul>
我使用不同的复选框 值 而不仅仅是布尔值,因为我想利用这样一个事实,即在表单 post 上,我会得到一个逗号- 选定 值的分隔列表。例如,如果同时选择科幻和恐怖,我会得到 "SF,HR"
.
查看模型
public class MovieViewModel
{
public string Name { get;set; }
public string Year { get;set; }
public IEnumerable<string> Genres { get;set; }
public IEnumerable<SelectListItem> GenreOptions { get;set; }
}
查看
在我看来,我想基本上这样做:
@Html.EditorFor(model => model.Name)
@Html.EditorFor(model => model.Year)
@Html.CheckBoxListFor(model => model.Genres, Model.GenreOptions)
自定义 Html 助手
所以,我开始创建一个自定义的 Html 助手,但我在这些方面没有太多经验,这就是我需要帮助的地方:
public static MvcHtmlString CheckboxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> items)
{
if (items == null) return null;
var itemsList = items.ToList();
if (!itemsList.Any()) return null;
// How do I get "Genres" name from the passed expression from viewmodel, without passing the hardcoded string "genres"?
var checkboxGroupName = expression.what????
var sb = new StringBuilder();
sb.Append("<ul>");
foreach (var item in itemsList)
{
var checkbox = $"<input type=\"checkbox\" name=\"{checkboxGroupName}\" value=\"{item.Value}\" />";
sb.Append($"<li class=\"checkbox\"><label>{checkbox} {HttpUtility.HtmlEncode(item.Text)}</label></li>");
}
sb.Append("</ul>");
return MvcHtmlString.Create(sb.ToString());
}
正如您在上面的评论中看到的,我不知道如何将视图模型 属性 名称 "Genres" 动态分配给复选框的 name
属性,而不对其进行硬编码.我如何从 model => model.Genres
表达式中获取它?
您可以从使用 ModelMetaData class 使用以下代码行:
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var checkboxGroupName = metadata.PropertyName;
这是一篇解释How to Create Custom Strongly Typed Html Helpers的文章,可能对您有更多帮助。
要获取 属性 名称,请使用
var name = ExpressionHelper.GetExpressionText(expression);
这将给出完全限定的名称,例如 @CheckboxListFor(m => m.Movies.Genres, ....)
将 return Movies.Genres
.
但是要考虑到您为模型使用自定义 EditorTemplate
的情况(这是父模型的 属性),那么您还需要获取 `HtmlFieldPrefix 并且如果它存在,在其前面加上名称。
您当前的代码没有为您提供正确的绑定,例如,如果 Genres
包含与 SelectListItem
的 Value
属性 相匹配的值,则关联的复选框应该是渲染视图时检查。你的代码应该是
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> items)
{
if (items == null)
{
throw new ArgumentException("...");
}
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var model = metadata.Model as IEnumerable<string>;
if (model == null)
{
throw new ArgumentException("...");
}
// Get the property name
var name = ExpressionHelper.GetExpressionText(expression);
// Get the prefix in case using a EditorTemplate for a nested property
string prefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix;
if (!String.IsNullOrEmpty(prefix))
{
name = String.Format("{0}.{1}", prefix, name);
}
StringBuilder html = new StringBuilder();
foreach (var item in items)
{
StringBuilder innerHtml = new StringBuilder();
TagBuilder checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("name", name);
checkbox.MergeAttribute("value", item.Value);
if (model.Contains(item.Value))
{
checkbox.MergeAttribute("checked", "checked");
}
innerHtml.Append(checkbox.ToString());
TagBuilder text = new TagBuilder("span");
text.InnerHtml = item.Text;
innerHtml.Append(text.ToString());
TagBuilder label = new TagBuilder("label");
label.InnerHtml = innerHtml.ToString();
TagBuilder li = new TagBuilder("li");
li.AddCssClass("checkbox");
li.InnerHtml = label.ToString();
html.Append(li);
}
TagBuilder ul = new TagBuilder("ul");
ul.InnerHtml = html.ToString();
return MvcHtmlString.Create(ul.ToString());
}