23 января 2013 г.

Создание глобальных расширений (helpers) в ASP.Net MVC, Часть 1

Давным-давно, в этом блоге я описывал несколько способов создания ссылки в виде изображения. Помнится, тогда я еще думал: "А ведь было бы круто, если бы в MVC имелся стандартный хэлпер, который наряду с ActionLink создавал бы ссылку в виде картинки, а не текста. Такой себе ActionImageLink!".

Признаюсь, в то время я был более глуп чем сейчас, и даже не думал о том, что хэлперы (расширения) можно создавать самому. Вернее, я думал, знал, но даже не пытался попробовать. Что же, пришло поставить на этом точку.

Сегодня мы научимся создавать глобальные расширения, и использовать их в представлениях нашего приложения.
Для начала, чтобы немного размяться, давайте построим простенькое расширение, которое будет выводить обычную картинку. Ну представим, что нам очень не нравится тег <img>, и мы не хотим его видеть в исходниках страницы. Тоесть мы сделаем так, что в представлении картинку можно будет поставить через @Html.Image().

Первым делом, нам необходимо создать в проекте статический класс, в котором будут описаны все наши дополнительные хэлперы. Я назвал этот класс MyHelpers, поместив его в папку Helpers. В этом классе, с помощью статического метода, описываем наше расширение:
   1:  public static class MyHelpers
   2:  {
   3:      public static MvcHtmlString Image(this HtmlHelper helper, 
                                               string srcUrl, string altText)
   4:      {
   5:          var builder = new TagBuilder("img");
   6:          builder.MergeAttribute("src", srcUrl);
   7:          builder.MergeAttribute("alt", altText);
   8:   
   9:          return MvcHtmlString.Create(
                                    builder.ToString(TagRenderMode.SelfClosing));
  10:      }
  11:  }

Название метода будет соответствовать названию расширения. Тип возвращаемого значения MvcHtmlString позволит отобразить результат в представлении в виде html, а не строки (по-умолчанию, для безопасности, razor вывел бы всё как строку). Первый параметр - стандартный для всех хэлперов, далее идут адрес изображения и его альтернативный текст. Как вы поняли, все содержимое хэлпера строится прямо здесь, в коде, с помощью обычных стринговых строк. Чаще всего, для построения используют StringBuilder, но я предпочитаю TagBuilder, который имеет ряд преимуществ перед первым, и заточен конкретно на работу с html-тэгами. Итак, мы создаем новый тег img, заполняем его атрибуты значениями принятых параметров, а в конце выдаем все наружу. SelfClosing - означает, что для img не нужен закрывающий тег, а закрытие происходит прямо в открывающем.

Теперь к представлению.
Первым делом мы должны перестроить решение, и подключить во View пространство имен, в котором лежат наши расширения. У меня это будет @using _MyMvcApplication.Helpers. Helpers - папка, в которой находится класс MyHelpers.cs.

Теперь можно вызывать наше расширение:

@Html.Image("https://dl.dropbox.com/u/4922774/foto_148731.jpg", "cat image")

В результате мы видим обычную картинку, как и хотели:




Ну что же, немного размялись. Теперь вернемся к нашей основной задаче...

Наше творение будет предназначено для отрисовки на странице ссылки в виде изображения. По-сути, результатом должны быть два тега: <img> внутри <a>.
Для начала договоримся, что наше будущее расширение будет принимать как параметры: источник изображения, альтернативный текст картинки, а также URL для перехода по ссылке. Назовем наш хэлпер ActionImageLink.

Итак, добавим в наш класс расширений (MyHelpers.cs) новый статический метод:

   1:  public static MvcHtmlString ActionImageLink(this HtmlHelper helper,
                                          string src, string altText, string url)
   2:  {
   3:      var image = new TagBuilder("img");
   4:      image.MergeAttribute("src", src);
   5:      image.MergeAttribute("alt", altText);
   6:   
   7:      var href = new TagBuilder("a");
   8:      href.AddCssClass("imagelink");
   9:      href.MergeAttribute("href", url);
  10:      href.InnerHtml = image.ToString(TagRenderMode.SelfClosing);
  11:   
  12:      return MvcHtmlString.Create(href.ToString());
  13:  }

Как и в разминочном случае, мы используем TagBuilder для построения тэгов. Но в этом случае их у нас 2, причем один вложен во второй. Поэтому мы и присвоили значение image, параметру ссылки InnerHtml. Таким образом, картинка попадет внутрь тега ссылки. Еще одно замечание - с помощью свойства AddCssClass в тэг можно добавлять любое кол-во css-классов. Также, обратите внимание, что SelfClosing не используется для тэга ссылки т.к. для него должен быть закрывающий тег </a>.

Пробуем использовать наш супер-хэлпер в представлении:

@Html.ActionImageLink("https://dl.dropbox.com/u/4922774/foto_148731.jpg",
                                    "cat image", "http://www.google.com.ua")

И вот окончательный результат в браузере:

Ссылка получилась, css-класс есть, изображение внутри тоже имеется. Всё сделано. Работа окончена. Созданное расширение можно использовать в любом представлении проекта.

Теперь о выводах.
Очень стыдно признать, но до этого времени я всего несколько раз создавал и использовал свои же хэлперы. Это мощнейший механизм, который позволяет делать безумно удобные и красивые вещи! Жаль, что я пришел к этому так поздно, но ведь хорошо, что пришел вообще. Теперь-то буду стараться уделять им достаточно внимания.

В следующей статье (следующей части), мы копнем чуть глубже в расширения, и попробуем создать что-то более сложное и полезное в больших проектах.
_____
Исходники