Расширение Html.ActionLink

Я пытаюсь расширить Html.ActionLink, поскольку хочу добавить настраиваемые метаданные для общего компонента (в данном случае модального).

Моя цель - еще больше расширить класс LinkExtensions в .Net MVC, который добавит значение в атрибут класса 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 есть перегрузки, которые принимают атрибуты html в форме IDictionary<string, object>, есть ли метод расширения, который преобразует анонимные типы в изменяемый словарь?

Все, что я получаю при поиске, - это использование метода Html.ActionLink ().


person Andy Clark    schedule 26.05.2015    source источник
comment
Почему бы не создать свой собственный custom HTML-помощник   -  person Icemanind    schedule 26.05.2015
comment
Я пытаюсь создать своего собственного помощника ... но вместо того, чтобы заново изобретать колесо, я также пытаюсь воспользоваться преимуществами встроенного помощника, который уже выполняет 80% работы. Как я уже сказал, мне просто нужно добавить к существующему параметру htmlAttributes.   -  person Andy Clark    schedule 26.05.2015


Ответы (2)


Функция, которую вы ищете:

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);
}
person SBurris    schedule 26.05.2015
comment
На первый взгляд, это то, что мне нужно, однако это не работает должным образом. Если посмотреть на сгенерированную разметку: она сгенерировала атрибуты, соответствующие свойствам в IDictionary ‹T, T› <a count="2" keys="System.Collections.Generic.Dictionary2 + KeyCollection [System.String, System.Object] values ​​= System.Collections.Generic.Dictionary2+ValueCollection[System.String,System.Object]" href="/Controller/Action/id">Testing</a> - person Andy Clark; 27.05.2015
comment
Только что понял, что вызов htmlHelper.ActionLink () все еще ссылался на htmlAttr как на объект типа, поэтому он генерировал разметку, как указано выше. Я изменил линию возврата на return htmlHelper.ActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), htmlAttr);, которая теперь работает. Я изменю ваш ответ, чтобы отразить это. - person Andy Clark; 27.05.2015
comment
В последней строке возврата, если нужно создать RouteValueDictionary, нам нужно проверить null на routeValues, чтобы избежать ошибки времени выполнения. Так должно быть return htmlHelper.ActionLink(linkText, actionName, controllerName, (routeValues != null ? new RouteValueDictionary(routeValues) : null), htmlAttr); В противном случае это может быть просто: return htmlHelper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttr); - person elimad; 28.10.2016

Некоторое время назад я сделал вспомогательный класс именно для такой ситуации. Это его базовая урезанная версия. Я оставил комментарии XML для одного из методов, потому что в противном случае это немного сбивает с толку.

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<string, object>.

Это также полезно, когда вы создаете свои собственные средства визуализации элементов управления HTML, поскольку вы можете использовать метод attributes.ToMvcHtmlString() для преобразования атрибутов в HTML.

person Octopoid    schedule 27.05.2015