【问题标题】:Refactor similar CHTML that renders varying properties using EditorFor and LabelFor重构使用 EditorFor 和 LabelFor 呈现不同属性的类似 CHTML
【发布时间】:2016-01-21 00:02:35
【问题描述】:

我正在构建一个项目,其中包含许多关于剃刀视图的通用代码。

例子:

<div class="form-group">
    @Html.LabelFor(model => model.LayoutFrontAmount, htmlAttributes: new { @class = "control-label col-xs-12 col-sm-4 col-md-3" })
    <div class="col-xs-4 col-sm-2 col-md-2 col-lg-1">
        @Html.EditorFor(model => model.LayoutFrontAmount, new { htmlAttributes = new { @class = "form-control" } })
    </div>
    <div class="col-xs-12 col-md-8 col-sm-offset-4 col-md-offset-3">
        <span class="help-block">@Html.ValidationMessageFor(model => model.LayoutFrontAmount, "", new { @class = "text-danger" })</span>
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.LayoutFrontBackAmount, htmlAttributes: new { @class = "control-label col-xs-12 col-sm-4 col-md-3" })
    <div class="col-xs-4 col-sm-2 col-md-2 col-lg-1">
        @Html.EditorFor(model => model.LayoutFrontBackAmount, new { htmlAttributes = new { @class = "form-control" } })
    </div>
    <div class="col-xs-12 col-md-8 col-sm-offset-4 col-md-offset-3">
        <span class="help-block">@Html.ValidationMessageFor(model => model.LayoutFrontBackAmount, "", new { @class = "text-danger" })</span>
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.LayoutTRC, htmlAttributes: new { @class = "control-label col-xs-12 col-sm-4 col-md-3" })
    <div class="col-xs-4 col-sm-2 col-md-2 col-lg-1">
        @Html.EditorFor(model => model.LayoutTRC, new { htmlAttributes = new { @class = "form-control" } })
    </div>
    <div class="col-xs-12 col-md-8 col-sm-offset-4 col-md-offset-3">
        <span class="help-block">@Html.ValidationMessageFor(model => model.LayoutTRC, "", new { @class = "text-danger" })</span>
    </div>
</div>

唯一改变的是模型的属性。

有没有办法将所有代码替换为:

@TemplateName1(model => model.LayoutFrontAmount)
@TemplateName1(model => model.LayoutFrontBackAmount)
@TemplateName1(model => model.LayoutTRC)

【问题讨论】:

  • 您可以创建一个 HtmlHelper 扩展方法来生成标签、表单控制和验证消息(以及相关的div 元素),您可以将其用作(例如)@Html.MyEditorFor(model =&gt; model.LayoutFrontAmount)
  • 谢谢@StephenMuecke,但我更喜欢最接近和更干净的 HTML 结构,它看起来很难理解和理解是什么
  • 抱歉,不确定您的意思。创建扩展方法后,您在视图中只需要 3 行代码 (@Html.MyEditorFor(m =&gt; m.someProperty)) 即可生成与您在问题中显示的完全相同的所有 html
  • 就像@Chase 的回答一样,我可以查看视图并查看 HTML 结构

标签: c# asp.net-mvc razor


【解决方案1】:

您可以创建一个 HtmlHelper 扩展方法,该方法将为属性生成所有 html,包括标签和输入元素

using System;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace YourAssembly.Html
{
  public static class BootstrapHelper
  {
    public static MvcHtmlString BootstrapEditorFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
    {      
      MvcHtmlString label = LabelExtensions.LabelFor(helper, expression, new { @class = "control-label col-xs-12 col-sm-4 col-md-3" });
      MvcHtmlString editor = EditorExtensions.EditorFor(helper, expression, new { htmlAttributes = new { @class = "form-control" } });
      MvcHtmlString validation = ValidationExtensions.ValidationMessageFor(helper, expression, null, new { @class = "text-danger" });

      // Build the input elements
      TagBuilder editorDiv = new TagBuilder("div");
      editorDiv.AddCssClass("col-xs-4 col-sm-2 col-md-2 col-lg-1");
      editorDiv.InnerHtml = editor.ToString();
      // Build the validation message elements
      TagBuilder validationSpan = new TagBuilder("span");
      validationSpan.AddCssClass("help-block");
      validationSpan.InnerHtml = validation.ToString();
      TagBuilder validationDiv = new TagBuilder("div");
      validationDiv.AddCssClass("col-xs-12 col-md-8 col-sm-offset-4 col-md-offset-3");
      validationDiv.InnerHtml = validationSpan.ToString();
      // Combine all elements
      StringBuilder html = new StringBuilder();
      html.Append(label.ToString());
      html.Append(editorDiv.ToString());
      html.Append(validationDiv.ToString());
      // Build the outer div
      TagBuilder outerDiv = new TagBuilder("div");
      outerDiv.AddCssClass("form-group");
      outerDiv.InnerHtml = html.ToString();
      return MvcHtmlString.Create(outerDiv.ToString());
    }
  }
}

然后你可以在你的web.config文件中注册它(意味着你不需要@using ...在视图中

<namespaces>
  <add namespace="System.Web.Mvc" />
  ....
  <add namespace="yourAssembly.Html " /> // add this
</namespaces>

现在在您的主视图中,您只需以下 3 行代码即可生成问题中显示的所有 html

@Html.BootstrapEditorFor(m => m.LayoutFrontAmount)
@Html.BootstrapEditorFor(m => m.LayoutFrontBackAmount)
@Html.BootstrapEditorFor(m => m.LayoutTRC)

如果您希望它可以在多个项目中重复使用,请将其编译到单独的 dll 中并在您的项目中添加对它的引用。

【讨论】:

  • 做得很好。旁注:虽然我相信这是实现 OP 所要求的唯一方法(因为您不能在 Razor 视图/帮助程序中真正通用),但我会认真考虑在此 C# 版本上保留重复的 HTML 标记。
  • @Stephen 非常感谢你的例子 :) 今天醒来时突然想到,如果我需要通过 jQuery 与元素交互,我没有机会做对吗?
  • @AlexeiLevenkov 嗨,重复的 HTML 标记是什么意思?
  • @Patrick 我的意思是我更喜欢你在问题中发布的 HTML 代码 - 虽然它有重复,但大多数人都可以一眼就能理解。查看斯蒂芬提供的代码,只有一小部分开发人员能够推断出结果输出是什么,或者能够快速调整它。在某些情况下是可以的(即相同的布局必须出现在大量的地方),因此需要仔细权衡哪种方法对特定案例/开发团队更可持续。
  • 它仍然创建与您问题中的代码完全相同的 html 输出(并且您的输入具有 id 属性),因此您可以以与以前完全相同的方式与 jQuery 交互。
【解决方案2】:

取决于 LayoutFrontAmount、LayoutFrontBackAmount 和 LayoutTRC 的类型。这些都是字符串吗?如果是这样,您可以有一个通用视图文件来存储用于显示每个模板的模板,然后在您的主视图中使用 @Html.Partial() 显示每个模板:

MyView.cshtml

@model string
<div class="form-group">
    @Html.LabelFor(model => Model, htmlAttributes: new { @class = "control-label col-xs-12 col-sm-4 col-md-3" })
    <div class="col-xs-4 col-sm-2 col-md-2 col-lg-1">
        @Html.EditorFor(model => Model, new { htmlAttributes = new { @class = "form-control" } })
    </div>
    <div class="col-xs-12 col-md-8 col-sm-offset-4 col-md-offset-3">
        <span class="help-block">@Html.ValidationMessageFor(model => Model, "", new { @class = "text-danger" })</span>
    </div>
</div>

然后在您的主视图文件中,您可以按如下方式渲染 LayoutFrontAmount、LayoutFrontBackAmount 和 LayoutTRC:

@Html.Partial("MyView", Model.LayoutFrontAmount);
@Html.Partial("MyView", Model.LayoutFrontBackAmount);
@Html.Partial("MyView", Model.LayoutTRC);

如果它们是不同的类型,那就是更大的挑战。

【讨论】:

  • 嗨,谢谢。挑战是指如果我有不同的类型,我需要创建一个“MyViewInt”和一个“MyViewString”?
  • 如果它们属于不同的类型,则很难将它们统一在一个模型视图下。如果您使用的只是整数和字符串,您可以轻松地将整数转换为字符串并将其传递给同一模型。但是,每个项目的编辑器都将基于视图顶部指定的模型类型。如果将 int 转换为字符串并使用字符串编辑器,则必须在编辑后从字符串中解析整数。
  • 与@Stephen 使用内置辅助方法的答案相比,性能如何?
  • 不确定对性能的影响,但在我看来,使用 HtmlHelper 扩展方法不太灵活且可维护性较差。每次您想要更改视图的显示方式时(这在实践中经常发生),都必须重新编译 HtmlHelper 扩展。如果您只使用 cshtml 文件执行纯基于视图的路由,则可以编辑文件而无需重新编译任何源代码。
猜你喜欢
  • 2023-03-26
  • 2012-06-03
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 2011-06-22
  • 1970-01-01
  • 2010-12-10
  • 1970-01-01
相关资源
最近更新 更多