【问题标题】:Html.ActionLink extensionHtml.ActionLink 扩展
【发布时间】:2015-08-08 08:17:27
【问题描述】:

我正在尝试扩展 Html.ActionLink,因为我想为共享组件(在本例中为模态)添加自定义元数据。

我的目标是进一步扩展 .Net MVC 中的 LinkExtensions 类,这将为 html 类属性添加一个值,并添加一个自定义数据属性,结果如下:

<a href="/Controller/Action/id" class="show-in-modal style1 style2" data-title="Modal title">Link</a>

帮助器看起来类似于 MVC 方法:

public static MvcHtmlString ModalLink(this HtmlHelper htmlHelper, string title, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
    // Add 'show-in-modal' class here
    // Add 'data-title' attribute here

    return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
}

@Html.ModalLink("Modal title", "Link", "action", "controller", new { id = "id" }, new { @class = "style1 style2" });

我遇到的这个问题是我不能轻易修改 htmlAttributes 对象来添加我的类名和数据属性,这是有道理的,因为这是一个只读匿名对象。

有没有一种方法可以让我轻松应用所需的值/元数据,而无需通过反射将所有内容分开并重新组合在一起?

我注意到 MVC 具有接受 IDictionary&lt;string, object&gt; 形式的 html 属性的重载,是否有将匿名类型转换为可修改字典的扩展方法?

我在搜索中得到的只是如何使用 Html.ActionLink() 方法。

【问题讨论】:

  • 为什么不创建自己的custom HTML helper
  • 我正在尝试创建自己的助手......但我并没有重新发明轮子,而是试图利用已经完成 80% 工作的内置助手。正如我所说,我只需要附加到现有的htmlAttributes 参数。

标签: c# asp.net-mvc razor html-helper tagbuilder


【解决方案1】:

你要找的功能是:

HtmlHelper.AnonymousObjectToHtmlAttributes()

https://msdn.microsoft.com/en-us/library/system.web.mvc.htmlhelper.anonymousobjecttohtmlattributes(v=vs.118).aspx

这是 ModalLink 扩展的一个版本:

public static MvcHtmlString ModalLink(this HtmlHelper htmlHelper, string title, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
  // Add 'show-in-modal' class here
  // Add 'data-title' attribute here

  var htmlAttr = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

  const string classKey = "class";
  const string titleKey = "data-title";

  const string classToAdd = "show-in-modal";
  if (htmlAttr.ContainsKey(classKey) == true)
  {
    htmlAttr[classKey] += " " + classToAdd;
  }
  else
  {
    htmlAttr.Add(classKey, classToAdd);
  }

  if (htmlAttr.ContainsKey(titleKey) == true)
  {
    htmlAttr[titleKey] = title;
  }
  else
  {
    htmlAttr.Add(titleKey, title);
  }

  return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), htmlAttr);
}

【讨论】:

  • 乍一看这似乎是我所需要的,但它并没有按预期工作。查看生成的标记时:它已生成与 IDictionary&lt;a count="2" keys="System.Collections.Generic.Dictionary2+KeyCollection[System.String,System.Object]" values="System.Collections.Generic.Dictionary 中的属性匹配的属性2+ValueCollection[System.String,System.Object]" href="/Controller/Action/id"&gt;Testing&lt;/a&gt;
  • 刚刚意识到对 htmlHelper.ActionLink() 的调用仍然将 htmlAttr 称为类型对象,这就是它生成上述标记的原因。我已将返回行更改为return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), htmlAttr);,现在可以使用。我会更改您的答案以反映这一点。
  • 在最后一个返回行中,如果必须创建RouteValueDictionary,那么我们需要为routeValues测试null以避免运行时错误。所以应该是return htmlHelper.ActionLink(linkText, actionName, controllerName, (routeValues != null ? new RouteValueDictionary(routeValues) : null), htmlAttr); 不然可以很简单:return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttr);
【解决方案2】:

不久前,我为这种情况创建了一个助手类。这是它的基本精简版。我将 XML cmets 留在了其中一种方法中,否则会有点混乱。

HtmlAttributes.cs

/// <copyright file="HtmlAttributes.cs"><author username="Octopoid">Chris Bellini</author></copyright>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web.Mvc;

public class HtmlAttributes : Dictionary<string, object>
{
    public HtmlAttributes()
        : base()
    {
    }

    public HtmlAttributes(object anonymousAttributes)
        : base(HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousAttributes))
    {
    }

    public HtmlAttributes(IDictionary<string, object> attributes)
        : base(attributes)
    {
    }

    public void Add(object anonymousAttributes)
    {
        this.Add(HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousAttributes));
    }

    public void Add(IDictionary<string, object> attributes)
    {
        foreach (var attribute in attributes)
        {
            this.Add(attribute.Key, attribute.Value);
        }
    }

    public void AddCssClass(string cssClass)
    {
        if (cssClass == null) { throw new ArgumentNullException("cssClass"); }

        string key = "class";
        if (this.ContainsKey(key))
        {
            string currentValue;
            if (this.TryGetString(key, out currentValue))
            {
                this[key] = currentValue + " " + cssClass;
                return;
            }
        }

        this[key] = cssClass;
    }

    public void Remove(object anonymousAttributes)
    {
        this.Remove(HtmlHelper.AnonymousObjectToHtmlAttributes(anonymousAttributes));
    }

    /// <summary>
    /// Removes the value with the specified key from the <see cref="System.Collections.Generic.Dictionary<TKey,TValue>"/>.
    /// This method hides the base implementation, then calls it explicity.
    /// This is required to prevent the this.Remove(object) method catching base.Remove(string) calls.
    /// </summary>
    /// <param name="key">The key of the element to remove.</param>
    /// <returns>
    /// true if the element is successfully found and removed; otherwise, false.
    /// This method returns false if key is not found in the System.Collections.Generic.Dictionary<TKey,TValue>.
    /// </returns>
    /// <exception cref="System.ArgumentNullException">key is null.</exception>
    public new bool Remove(string key)
    {
        return base.Remove(key);
    }

    public void Remove(IDictionary<string, object> attributes)
    {
        foreach (var attribute in attributes)
        {
            this.Remove(attribute.Key);
        }
    }

    public MvcHtmlString ToMvcHtmlString()
    {
        return new MvcHtmlString(this.ToString());
    }

    public override string ToString()
    {
        StringBuilder output = new StringBuilder();

        foreach (var item in this)
        {
            output.Append(string.Format("{0}=\"{1}\" ", item.Key.Replace('_', '-'), item.Value.ToString()));
        }

        return output.ToString().Trim();
    }

    public bool TryGetString(string key, out string value)
    {
        object obj;
        if (this.TryGetValue(key, out obj))
        {
            value = obj.ToString();
            return true;
        }
        value = default(string);
        return false;
    }
}

在你的情况下,在你的辅助方法中,你会这样做:

HtmlAttributes finalAttributes = new HtmlAttributes(htmlAttributes);
finalAttributes.Add("data_title", "title");
finalAttributes.AddCssClass("show-in-modal");

注意,如果需要,您也可以批量添加(或删除)它们:

finalAttributes.Add(new { data_title = "title", id = "id", data_extra = "extra" });

然后你可以像往常一样传入 finalAttributes,因为它扩展了 Dictionary&lt;string, object&gt;

这在您制作自己的自定义 HTML 控件渲染器时也很有用,因为您可以使用 attributes.ToMvcHtmlString() 方法将属性渲染为 HTML。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-25
    • 2020-09-10
    • 2014-09-23
    • 1970-01-01
    • 2020-12-11
    • 1970-01-01
    相关资源
    最近更新 更多