使用HTML助手减少代码重复
本文关键字:代码 HTML 使用 | 更新日期: 2023-09-27 18:21:08
我有一个用ASPMVC编写的相当简单的数据审计web应用程序,它有效地具有用于不同目的的同一模型的两个视图。
-
代理视图-由验证信息的人员填写的表格。此视图中表单上的每个字段都有3个子字段:
a。原始值-调用之前数据库中的值
b。新价值-如果与原始价值不同,则个人在电话中提供的价值。
c。行动-发生的事情的一般指示
-
QC视图-由审查在Agent视图中执行的工作的人员填写的表格。此视图中表单上的每个字段都有5个子字段:
a。原值-与上述相同
b。代理人价值-代理人在上述1b中提供的价值。
c。QC值-如果代理人指定的值不正确,则为校正后的"新值"。
d。代理操作-与上述相同,但在此视图中只读除外
e。QC行动-如果代理人选择不当,则纠正"新行动"。
这两个视图之间的唯一区别是可用的子字段。我希望能够使用一个视图来表示两个视图,因为页面的整体结构是相同的,并且只使用HTML助手来处理子字段中的差异。到目前为止,我有两个明显独立的助手系列(目前在同一个类中,但可能是分开的):
// Agent controls
public static MvcHtmlString AuditControl(this HtmlHelper htmlHelper, string id, string fieldLabel, MvcHtmlString editControl, string cssClass)
public static MvcHtmlString AuditControl(this HtmlHelper htmlHelper, string id, string fieldLabel, string editControl, string cssClass)
public static MvcHtmlString AuditControl<COMPLEX>(this HtmlHelper htmlHelper, string id, string fieldLabel, string cssClass) where COMPLEX : AbstractComplex, new()
// QC controls
public static MvcHtmlString ReviewControl(this HtmlHelper htmlHelper, string id, string fieldLabel, MvcHtmlString editControl, string cssClass)
public static MvcHtmlString ReviewControl(this HtmlHelper htmlHelper, string id, string fieldLabel, string editControl, string cssClass)
public static MvcHtmlString ReviewControl<COMPLEX>(this HtmlHelper htmlHelper, string id, string fieldLabel, string cssClass) where COMPLEX : AbstractComplex, new()
第三种实现处理由多条数据组成的更复杂的字段(如全名、地址等)。
我考虑过的一种可能的解决方案是将不同类型的控件分离为实现公共接口的不同类,然后将它们作为类型参数传递给更通用的HTML助手。我认为这会起作用,但我需要能够告诉视图应该使用哪个实现来绘制视图,这似乎有问题,因为它似乎模糊了视图和控制器之间的界限。
一种似乎不那么吸引人的方法是从控制器传递一种管理标志,该标志将由泛型(在逻辑中不是指泛型类型)工厂助手使用,并在其中构建逻辑,以知道要使用哪一系列方法。这将使模型和视图保持分离,但感觉很脏,因为这样HTML助手将不仅仅负责构建HTML。
这是打破MVC设计的关注点分离的合理情况吗?还是有更合适的解决方案?
由于您使用的是MVC3,我建议对子字段使用子操作:
http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx
子操作允许您在视图内的控制器上执行操作,这将是一种更干净的方法。
我能够相当简单地实现@SoWeLie提供的建议。它涉及创建一个新的模型,以容纳可能的控制属性的超集,并为每个不同的控制集绘制一个新视图(一个用于审核,一个用于审查)。它的问题是由此产生的View API很丑陋:
@Html.RenderAction("DrawControl", new { id = "ID" ... })
// Repeated for all of the overloads of DrawControl
控制器的每个动作都包含类似的内容:
public ActionResult DrawControl(string id, ...)
{
// FieldControl being the name of my Model
var viewModel = new FieldControl() { ID = id, ... };
if (shouldRenderAudit)
return PartialView("AuditControl", viewModel);
else
return PartialView("ReviewControl", viewModel);
我不知道如何让我的通用助手在这种情况下工作,此外,我想删除减少明显的代码重复,所以很快就变成了:
@functions {
public string DrawControl(string id, ...)
{
return Html.Render("DrawControl", new { id = "ID" });
}
// Repeated for all of the overloads of DrawControl
}
@DrawControl("ID", ...)
使用相同的控制器操作。这个问题(忽略了View有函数的事实)是,@functions
块必须包含在任何想要使用它们的视图中(目前只有2,但很快就会膨胀到5,谁知道我的前任会怎么做)。我很快又重新编写了代码,这次是为了带回帮助程序(通常保留视图、模型和控制器的更改),最后得到了这样的结果:
视图:
@(Html.DrawComplexControl<ProviderName>("id", ...))
@Html.DrawSimpleControl("id", ...)
控制器:
// One common action that is used to determine which control should be drawn
public ActionResult DrawControl(FieldControl model)
{
if (shouldRenderAudit)
return PartialView("AuditControl", model);
else
return PartialView("ReviewControl", model);
}
助手:
public static MvcHtmlString DrawControl(this HtmlHelper htmlHelper, string id, ...)
{
var model = new FieldControl() { ID = id, ... };
return htmlHelper.Action("DrawControl", model);
}
public static MvcHtmlString DrawSimpleControl(this HtmlHelper htmlHelper, string id, ...)
{
return DrawSimpleControl(htmlHelper, id, ...);
}
public static MvcHtmlString DrawSimpleControl(this HtmlHelper htmlHelper, string id, ...)
{
// Set some defaults to simplify the API
return DrawControl(htmlHelper, id, ...);
}
public static MvcHtmlString DrawComplexControl<T>(this HtmlHelper htmlHelper, string id, ...) where T : AbstractComplex, new()
{
// Build the required controls based on `T`
return DrawControl(htmlHelper, id, ...);
}
当然,在这些迭代之间还有大约六次其他迭代可以帮助解决这种情况,但没有一次达到必要的程度。我相信还有改进的地方,但这就是我到目前为止所做的。
这样做为View提供了一个非常简单的API来使用,而无需了解或关心实现,并且它只需微小的修改(至少最终)就可以满足我之前存在的API的所有要求。我不确定这是否是结果的答案,但它是功能性的,并提供了必要的简单性。
希望我的头痛将来能帮助其他人。