【问题标题】:Add to htmlAttributes for custom ActionLink helper extension添加到自定义 ActionLink 帮助程序扩展的 htmlAttributes
【发布时间】:2014-01-17 14:42:56
【问题描述】:

我正在尝试创建 Html.ActionLink(...) HtmlHelper 的简单自定义版本

我想为传入的 htmlAttributes 匿名对象附加一组额外的属性。

public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
    var customAttributes = new RouteValueDictionary(htmlAttributes) {{"rel", "nofollow"}};
    var link = htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, customAttributes);
    return link;
}

所以在我看来我会这样:

@Html.NoFollowActionLink("Link Text", "MyAction", "MyController")

我希望呈现如下链接:

<a href="/MyController/MyAction" rel="nofollow">Link Text</a>

但是我得到了:

<a href="/MyController/MyAction" values="System.Collections.Generic.Dictionary`2+ValueCollection[System.String,System.Object]" keys="System.Collections.Generic.Dictionary`2+KeyCollection[System.String,System.Object]" count="1">Link Text</a>

我尝试了各种将匿名类型转换为 RouteValueDictionary 的方法,将其添加到根 ActionLink(...) 方法或转换为 Dictionary,或使用 HtmlHelper.AnonymousObjectToHtmlAttributes 并做同样的事情,但似乎没有任何效果。

【问题讨论】:

    标签: c# asp.net .net asp.net-mvc-4


    【解决方案1】:

    你得到的结果是这个来源code造成的:

    public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
    {
        return ActionLink(htmlHelper, linkText, actionName, controllerName, TypeHelper.ObjectToDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }
    

    如您所见,HtmlHelper.AnonymousObjectToHtmlAttributes 在内部被调用。这就是为什么在传递 RouteValueDictionary 对象时会得到 valueskeys 的原因。

    只有两种方法匹配您的参数列表:

    public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
    public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
    

    第二个重载不会对您的参数做任何事情,只是传递它们。 您需要修改代码以调用其他重载:

    public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues = null, object htmlAttributes = null)
    {
        var routeValuesDict = new RouteValueDictionary(routeValues);
    
        var customAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
        if (!customAttributes.ContainsKey("rel"))
            customAttributes.Add("rel", "nofollow");
    
        return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValuesDict, customAttributes);
    }
    

    当您将routeValues 作为RouteValueDictionary 传递时,会选择另一个重载(RouteValueDictionary 正在实现IDictionary&lt;string, object&gt;,所以这很好),并且返回的链接是正确的。

    如果rel 属性已经存在于htmlAttributes 对象中,则会抛出异常:

    System.ArgumentException: An item with the same key has already been added.
    

    【讨论】:

    • 谢谢@slawek - 经过漫长的一周后,答案让我望而却步,这是一种享受。疯狂的 ActionLink 有多少重载。
    【解决方案2】:

    由于您想更新收到的 htmlAttributes 对象以便添加新属性 (rel),因此您需要将匿名 htmlAttributes 对象转换为 IDictionary&lt;string,object&gt;(因为您无法向匿名对象添加新属性)。

    这意味着您需要调用ActionLink 方法的this overload,这还需要将匿名routeValues 转换为RouteValueDictionary

    您可以使用new RouteValueDictionary(routeValues) 轻松转换路由值。要转换 html 属性,您将需要一些反射逻辑,例如 as in this question。 (正如 slawek 在他的回答中已经提到的,您还可以利用 RouteValueDictionary 已经实现字典并以相同方式转换 htmlAttributes)

    你的扩展最终会是这样的:

    public static MvcHtmlString NoFollowActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues = null, object htmlAttributes = null)
    {
        var htmlAttributesDictionary = new Dictionary<string, object>();
        if (htmlAttributes != null)
        {
            foreach (var prop in htmlAttributes.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                htmlAttributesDictionary.Add(prop.Name, prop.GetValue(htmlAttributes, null));
            }
        }
        htmlAttributesDictionary["rel"] = "nofollow";
    
        var routeValuesDictionary = new RouteValueDictionary(routeValues);
    
        var link = htmlHelper.ActionLink(linkText, actionName, controllerName, routeValuesDictionary, htmlAttributesDictionary);
        return link;
    }
    

    如果你这样称呼它:

    @Html.NoFollowActionLink("Link Text", "MyAction", "MyController", new { urlParam="param1" }, new { foo = "dummy" })
    

    你会得到以下html:

    <a foo="dummy" href="/MyController/MyAction?urlParam=param1" rel="nofollow">Link Text</a>
    

    注意,因为它是在将原始属性添加到字典后添加/更新 rel 属性,所以即使调用者为该属性指定了另一个值,它也会始终强制 rel="nofollow"

    希望对你有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-26
      • 2021-09-25
      • 1970-01-01
      • 2014-12-06
      • 1970-01-01
      • 2011-08-25
      相关资源
      最近更新 更多