热门:网页模板.net视频教程JQueryMVCjsonExtJs源码示例三级联动JQuery菜单
您现在的位置:.Net中文社区>> .Net编程>>正文内容

我对ASP.NET MVC HtmlHelper中的Form和Link的小扩展

发布时间:2011年04月01日点击数: 佚名

项目中碰到的问题,或多或少的记录下来,一是积累,二是各位大牛给提供更好的建议。

1、HtmlHelper中的Link

您在开发web程序的时候很有可能会定义一个执行JavaScript的伪链接(这是我起的名字),例如:

  1. <a href="javascript:void(0)" onclick="alert('hello world')">Click me</a> 

之所以我喊它为伪链接是因为它并不会跳转页面(当然那种定义location.href的除外)。

问题出现在这个伪链接并不是一成不变的,是根据数据状态来确定的,例如,当某一列的值为0的时候显示该连接,为1的时候不显示该连接,

那么我们可以这么写(示例而已):

  1. <%if (1 == 1) 
  2. {%> 
  3. <a href="javascript:void(0)" onclick="alert('hello world')">Click me</a> 
  4. <%} %> 
  5. <%else 
  6. { %> 
  7. 其它的东西。。。 
  8. <%} %> 

很好,假如我们的状态有很多种呢,你可能要用到switch,然后还会套嵌if else,那么我们的view无疑会变得Tag soup,使维护变得异常的麻烦。

还好,我们可以为HtmlHelper扩展一个方法,将这些逻辑都写到扩展方法里面。(可能有朋友说将这些链接放到独立的ViewUserControl中,但是依然不能解决问题,因为那会使我们的ViewUserControl变得和view一样)

写到方法里面的链接必然要用到HtmlHelper的方法:Html.LinkExtensions.ActionLink,这个方法几乎能够提供我们使用html标签时所有形式。为什么

要用几乎,是因为对应上面的伪链接的形式,它支持的并不好。我在查看了起源码之后,并没有找到合适的方法能让ActionLink生成类似:

  1. <a href="javascript:void(0)" onclick="alert('hello world')">Click me</a> 

的链接。所以我决定实现一个专门生成这种伪链接的ActionLink方法。和MVC自带的ActionLink一样,使用其TagBuilder很容易实现该操作:

  1. /// <summary> 
  2.  /// 生成类似a href="javascript:void(0) onclick="alert('click me');">Click Me /a 链接 
  3.  /// </summary> 
  4.  /// <param name="htmlHelper"></param> 
  5.  /// <param name="linkText"></param> 
  6.  /// <param name="htmlAttributes"></param> 
  7.  /// <returns></returns> 
  8.  public static string ActionLinkForJS(this HtmlHelper htmlHelper, string linkText, object htmlAttributes) 
  9.  return ActionLinkForJS(htmlHelper, linkText, new RouteValueDictionary(htmlAttributes)); 
  10.  } 
  11.  
  12.   /// <summary> 
  13.   /// 生成类似a href="javascript:void(0) onclick="alert('click me');">Click Me /a 链接 
  14.   /// </summary> 
  15.   /// <param name="htmlHelper"></param> 
  16.   /// <param name="linkText"></param> 
  17.   /// <param name="htmlAttributes"></param> 
  18.   /// <returns></returns> 
  19.   public static string ActionLinkForJS(this HtmlHelper htmlHelper, string linkText, IDictionary<stringobject> htmlAttributes) 
  20.  { 
  21.  if (String.IsNullOrEmpty(linkText)) 
  22.  { 
  23.  throw new ArgumentException("linkText"); 
  24.  } 
  25.  
  26.  TagBuilder linkTag = new TagBuilder("a"); 
  27.  linkTag.InnerHtml = linkText; 
  28.  linkTag.MergeAttribute("href""javascript:void(0)"); 
  29.  linkTag.MergeAttributes(htmlAttributes); 
  30.  
  31.  return linkTag.ToString(TagRenderMode.Normal); 
  32.  } 

这样就实现了让ActionLink生成伪链接。


2、HtmlHelper中的BeginForm

曾几何时,我一直在用HtmlHelper中的BeginForm来生成一个Form表单。最近的一次使用出现了问题,代码是这样的:

  1. <%using (Html.BeginForm("List""Complaint"null, FormMethod.Post, null)) 

因为页面有不少查询条件,类似于搜索。我会把搜索条件Post到Action里面:

  1. public ActionResult List(ComplaintInfo paramComplaint) 

页面的查询条件类似于:

请求List这个Action的时候如果传递一个id过去,会查询出来对应的一条数据,对应的URL是这样的:http://localhost:8886/Complaint/List/220

问题就出现在这里,页面生成Form标签的action属性是这样的:

  1. <form action="/Complaint/List/220" method="post"> 

这种在Post到action的时候(请注意上面的List方法),会对参数进行ModelBinder,进而相当于传递了一个参数id过去,这样其它的查询条件

就失去了作用(因为id是主键)。我尝试将BeginForm的参数routeValues显示定义为null,问题依然存在。我现在就想根据传递的actionName,controllerName

进而拼接对应Form的action属性。例如,我上面的BeginForm方法应该生成统一的形式:

  1. <form action="/Complaint/List" method="post"> 

 

而不是在把RouteValue一起放置在Form的action属性里面。

我仔细的查看了BeginForm的源码,发现问题出现在

UrlHelper方法

  1. internal static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) { 
  2. RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues); 
  3.  
  4. VirtualPathData vpd = routeCollection.GetVirtualPath(requestContext, routeName, mergedRouteValues); 
  5. if (vpd == null) { 
  6. return null
  7.  
  8. string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath); 
  9. return modifiedUrl; 

中使用Route组件得到VirtualPath方法,会自动将RouteData匹配到相应的URL,导致生成的Form的action属性并不会随我们所欲。知道了问题,

改起来也很容易:

  1.  public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, object routeValues, FormMethod method, object htmlAttributes) 
  2.  { 
  3.  return BeginForm(htmlHelper, actionName, controllerName, new RouteValueDictionary(routeValues), method, new RouteValueDictionary(htmlAttributes)); 
  4.  } 
  5.  
  6.   public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, FormMethod method, IDictionary<stringobject> htmlAttributes) 
  7. TagBuilder tagBuilder = new TagBuilder("form"); 
  8. tagBuilder.MergeAttributes(htmlAttributes); 
  9.  // action is implicitly generated, so htmlAttributes take precedence. 
  10.    tagBuilder.MergeAttribute("action"string.Format("/{0}/{1}", controllerName, actionName)); 
  11.  // method is an explicit parameter, so it takes precedence over the htmlAttributes. 
  12.    tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true); 
  13.  
  14.  HttpResponseBase httpResponse = htmlHelper.ViewContext.HttpContext.Response; 
  15.  httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag)); 
  16.  return new MvcForm(htmlHelper.ViewContext.HttpContext.Response); 
  17.  } 

其实还有一种更简单直接的方法,就是不用BeginForm方法,而是直接写html:

<form action="/Complaint/List" method="post">

到此便结束了(其实您可以利用tagbuilder类扩展很多东西出来),不知道您是否有更好的方案呢?

本站热点业务

更多模板/案例展示

关于我们 | 联系我们 | 团队日志 | 网站地图 | 网站合作