Данная статья посвящается созданию ссылки картинкой с использованием хелпер метода Ajax.ActionLink, а точнее Extension методу Ajax.ActionImageLink, который мы и будем создавать.
Для начала краткие сведения о Ajax, ASP .NET MVC 3 и их совместном использовании.
AJAX (Asynchronous Javascript and XML — «асинхронный JavaScript и XML») — подход к построению интерактивных пользовательских интерфейсов веб-приложений, заключающийся в «фоновом» обмене данными браузера с веб-сервером. В результате, при обновлении данных, веб-страница не перезагружается полностью, и веб-приложения становятся более быстрыми и удобными.
ASP .NET MVC — фреймворк для создания веб-приложений, который реализует шаблон Model-view-controller (MVC) на базе технологии ASP .NET.
Для использования AJAX в ASP .NET MVC 3 существует несколько Action методов, но нас интересует Ajax.ActionLink, поэтому о нём поподробнее:
Ajax.ActionLink — метод, возвращающий сформированную в зависимости от передаваемых параметров ссылку, клик по которой отправит асинхронный запрос на сервер и определит дальнейшее поведение страницы.
Всего существует 12 вариантов перегрузки этого метода,- ниже рассмотрены входные параметры, которые могут быть переданы для его использования:
Для использования возможностей Ajax в проекте MVC 3 нам требуется подключить следующие файлы, которые, по умолчанию, находятся в папке /Scripts:
Требуется создать ссылку в виде изображения (в нашем случае звездочки), клик по которой вызовет асинхронный запрос на сервер, и после получения ответа, выполнится её обновление (смена изображения, всплывающей подсказки):

Рассмотрев возможности, которые предоставляет нам Microsoft по работе с Ajax хелпером ActionLink, приходим к выводу, чтобез велосипеда здесь не обойтись их не достаточно, поэтому нам потребуется наш собственный хелпер. Приступим.

Во View engine указываем Razor.


Выбираем ~/Views/Shared/_Layout.cshtml как мастер пейдж для нашего представления

И так, теперь нам требуется создать Extension метод для нашего Ajax хелпера.
Добавляем в корень проекта папку Core и в неё новый статический класс Extensions.cs

Подробнее о создании расширяющих методов можно прочитать здесь.
Переходим непосредственно к созданию расширяющего метода. И так, за основу нашей ссылки с изображением возьмём HTML разметку и стили habrahabr.ru, тогда содержимое контейнера будет представлено двумя состояниями:
Отсекая всё лишнее, приходим к тому, что контейнером (объектом для изменения — UpdateTargetId) будет являться div, а возвращать наш метод должен будет HTML разметку в виде ссылки с изменяющимся классом и title'ом.
*Тут автор впервые задумался, и понял, что всё это решается и без расширяющих методов посредством следующего кода:
но решив, что так будет слишком просто, и немного смекнув, усложнил себе задачу.*
Отсекая всё лишнее… Добавляя лишнее, приходим к тому, что контейнером (объектом для изменения — UpdateTargetId) будет являться div, а возвращать наш метод должен будет HTML разметку в виде ссылки, содержащей в себе изображение с изменяющимся title'ом и путём до картинки.
Примечание:
Можно использовать разные методы для добавления/удаления записи в избранное, но в рамках этой статьи метод будет использоваться один — AddOrRemoveFavourite.
И так, каркас нашего расширяющего метода представлен ниже:
Входными параметрами являются:
Для создания HTML разметки нашего изображения воспользуемся классом TagBuilder:
Для создания ссылки используем стандартный метод Ajax.ActionLink:
и затем добавим замену текста ссылки, на содержимое нашего HTML элемента img:
В результате, наш класс Extensions.cs примет вид:
Если добавить в представление вызов нашего расширяющего метода (предварительно указав директиву @using MVC3_AjaxActionImageLink.Core), то будет сформирована следующая HTML разметка:
или
Осталось добавить лишь div, картинки звездочек, вызов метода в представлении и сам метод AddOrRemoveFavourite, однако на последнем хотелось бы остановиться поподробнее.
В общем виде, метод, который обрабатывает асинхронный запрос, возвращает значение, будь то строка, число, JSON данные и пр., но в нашем случае, нам потребуется вернуть HTML разметку нового элемента, который генерирует наш расширяющий метод. Увы (или к счастью), в ASP .NET MVC 3 отсутствует возможность вызова нашего расширяющего метода на стороне сервера (в методе контроллера) и поэтому сгенерировать разметку без дублирования самого кода в методе контроллера у нас не получится (иначе, будет нарушен один из основополагающих принципов MVC- DRY).
Однако, из метода контроллера мы можем вернуть вызов Partial View, который инициирует вызов нашего расширяющего метода, который в свою очередь вернёт HTML разметку.
Приступим.
Добавляем в папку Views папку Partial. В Partial добавляем папку Favourites.
В папку Favourites добавляем View, выбирая галкой Create as partial view и указывая наименование AddOrRemoveFavourites_PartialView

В содержимое добавленного Partial View добавляем директивы:
и сам вызов нашего метода:
Примечание. Для упрощения деталей моделью в данной статье будет являться статическая переменная типа bool.
Содержимое же Index.cshtml теперь поручает вызов расширяющего метода Partial View и выглядит следующим образом:
Для того, чтобы вернуть наш Partial View, требуется указать в return вызов метода PartialView, передав первым параметром путь. Кроме этого, так как мы сохраняем состояние примитивным способом через статическую bool переменную, мы передадим её как модель в обе наши View. Конечный вариант нашего класса HomeController.cs представлен ниже:
Завершающим этапом будет добавление папки images с картинками star-off.png и star-on.png, а так же подключение скрипта для работы с AJAX в ASP .NET MVC 3:
Пробуем.

кликаем


Итак, что же мы всё таки в данной статье сделали и усвоили:
Наименование метода и принцип построения ссылки в виде изображения был взят из проекта Stephen Walthe's Contact manger project
Для начала краткие сведения о Ajax, ASP .NET MVC 3 и их совместном использовании.
AJAX (Asynchronous Javascript and XML — «асинхронный JavaScript и XML») — подход к построению интерактивных пользовательских интерфейсов веб-приложений, заключающийся в «фоновом» обмене данными браузера с веб-сервером. В результате, при обновлении данных, веб-страница не перезагружается полностью, и веб-приложения становятся более быстрыми и удобными.
ASP .NET MVC — фреймворк для создания веб-приложений, который реализует шаблон Model-view-controller (MVC) на базе технологии ASP .NET.
Для использования AJAX в ASP .NET MVC 3 существует несколько Action методов, но нас интересует Ajax.ActionLink, поэтому о нём поподробнее:
Ajax.ActionLink
Ajax.ActionLink — метод, возвращающий сформированную в зависимости от передаваемых параметров ссылку, клик по которой отправит асинхронный запрос на сервер и определит дальнейшее поведение страницы.
Всего существует 12 вариантов перегрузки этого метода,- ниже рассмотрены входные параметры, которые могут быть переданы для его использования:
- this AjaxHelper ajaxHelper — класс, предоставляющий поддержку отображения HTML в AJAX сценариях;
- string linkText — текст ссылки;
- string actionName — название серверного Action метода контроллера, куда поступит запрос на обработку;
- string controllerName — название контроллера, куда поступит запрос в поисках Action метода для обработки;
- string protocol — протокол, по которому будет отправлен запрос (например «http» или «https»);
- string hostName — доменное имя (например «habrahabr.ru»);
- string fragment — наименование фрагмента, которое добавится после # в конец URL'a;
- RouteValueDictionary / object routeValues — параметры, передаваемые на сервер в строке URL'a;
- AjaxOptions ajaxOptions — опции выполнения асинхронного запроса
- string Confirm — сообщение, которое отобразится перед отправкой данных на сервер с требованием подтверждения действий пользователем;
- string HttpMethod — метод, которым отправлять данные на сервер («POST», «GET»);
- InsertionMode InsertionMode — свойство, указывающее как обновлять DOM HTML страницы полученными данными («InsertAfter», «InsertBefore», or «Replace»);
- string LoadingElementId — id HTML элемента, который отображается пока выполняется асинхронный запрос (от OnBegin до OnComplete), например, это может быть ajaxLoader.gif;
- string OnBegin — название javascript функции, которая будет выполнена до отправки запроса;
- string OnComplete — название javascript функции, которая будет выполнена после того, как данные поступили от сервера, но страница еще не была обновлена;
- string OnFailure — название javascript функции, которая выполнится в случае ошибки обновления страницы. Например, можно перехватить код ошибки, который вернёт сервер;
- string OnSuccess — название javascript функции, которая будет выполнена после успешного получения данных от сервера и обновления страницы;
- string UpdateTargetId — id HTML элемента, который будет обновлён полученными данными от сервера (например HTML разметкой или строкой обновить div или вывести сообщение в span);
- string Url — URL адрес, на который отправить запрос;
- IDictionary<string, Object> htmlAttributes — коллекция ключ-значение, в которой можно указать добавляемые к сгенерированной HTML разметке аттрибуты (например new { class = «add» }).
Для использования возможностей Ajax в проекте MVC 3 нам требуется подключить следующие файлы, которые, по умолчанию, находятся в папке /Scripts:
- jquery-1.4.4.js
- jquery.unobtrusive-ajax.min.js
Постановка задачи
Требуется создать ссылку в виде изображения (в нашем случае звездочки), клик по которой вызовет асинхронный запрос на сервер, и после получения ответа, выполнится её обновление (смена изображения, всплывающей подсказки):

Рассмотрев возможности, которые предоставляет нам Microsoft по работе с Ajax хелпером ActionLink, приходим к выводу, что
1. Создаем пустой ASP .NET MVC 3 проект

Во View engine указываем Razor.

2. Добавляем HomeController и представление для метода Index

Выбираем ~/Views/Shared/_Layout.cshtml как мастер пейдж для нашего представления

И так, теперь нам требуется создать Extension метод для нашего Ajax хелпера.
3. Создаем Extension метод Ajax.ActionImageLink
Добавляем в корень проекта папку Core и в неё новый статический класс Extensions.cs

Подробнее о создании расширяющих методов можно прочитать здесь.
Переходим непосредственно к созданию расширяющего метода. И так, за основу нашей ссылки с изображением возьмём HTML разметку и стили habrahabr.ru, тогда содержимое контейнера будет представлено двумя состояниями:
<div class="favourite">
<a class="add" title="Добавить в избранное" href="#"></a>
</div>
<div class="favourite">
<a class="remove" title="Удалить из избранного" href="#"></a>
</div>
Отсекая всё лишнее, приходим к тому, что контейнером (объектом для изменения — UpdateTargetId) будет являться div, а возвращать наш метод должен будет HTML разметку в виде ссылки с изменяющимся классом и title'ом.
*Тут автор впервые задумался, и понял, что всё это решается и без расширяющих методов посредством следующего кода:
@Ajax.ActionLink(" ", "Hello", "World", null, new AjaxOptions() { },
new {
@title= Model.inFavourite? "Добавить в избранное" : "Удалить из избранного",
@class = Model.inFavourite? "add" : "remove"
})
но решив, что так будет слишком просто, и немного смекнув, усложнил себе задачу.*
<div id="favourite">
<a href="/Home/AddOrRemoveFavourite">
<img src="/Content/images/star-off.png" title="Добавить в избранное" />
</a>
</div>
<div id="favourite">
<a href="/Home/AddOrRemoveFavourite">
<img src="/Content/images/star-on.png" title="Удалить из избранного" />
</a>
</div>
Примечание:
Можно использовать разные методы для добавления/удаления записи в избранное, но в рамках этой статьи метод будет использоваться один — AddOrRemoveFavourite.
И так, каркас нашего расширяющего метода представлен ниже:
public static IHtmlString ImageActionLink(this AjaxHelper helper, string actionName, bool inFavourite, AjaxOptions ajaxOptions)
{
}
Входными параметрами являются:
- actionName — наименование метода контроллера (AddOrRemoveFavourite);
- inFavourite — признак того, добавлена ли запись уже в избранное;
- ajaxOptions — параметры выполнения запроса.
Для создания HTML разметки нашего изображения воспользуемся классом TagBuilder:
var builder = new TagBuilder("img");
builder.MergeAttribute("src", String.Format("/Content/images/star-{0}.png", inFavourite ? "on" : "off"));
builder.MergeAttribute("title", inFavourite ? "Удалить из избранного" : "Добавить в избранное");
Для создания ссылки используем стандартный метод Ajax.ActionLink:
var link = helper.ActionLink("[replaceme]", actionName, routeValues, ajaxOptions).ToHtmlString();
и затем добавим замену текста ссылки, на содержимое нашего HTML элемента img:
return new MvcHtmlString(link.Replace("[replaceme]", builder.ToString(TagRenderMode.SelfClosing)));
В результате, наш класс Extensions.cs примет вид:
public static class Extensions
{
public static IHtmlString ImageActionLink(this AjaxHelper helper, string actionName, bool inFavourite, AjaxOptions ajaxOptions)
{
var builder = new TagBuilder("img");
builder.MergeAttribute("src", String.Format("/Content/images/star-{0}.png", inFavourite ? "on" : "off"));
builder.MergeAttribute("title", inFavourite ? "Удалить из избранного" : "Добавить в избранное");
var link = helper.ActionLink("[replaceme]", actionName, inFavourite, ajaxOptions).ToHtmlString();
return new MvcHtmlString(link.Replace("[replaceme]", builder.ToString(TagRenderMode.SelfClosing)));
}
}
Если добавить в представление вызов нашего расширяющего метода (предварительно указав директиву @using MVC3_AjaxActionImageLink.Core), то будет сформирована следующая HTML разметка:
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#favourite" ref="/Home/AddOrRemoveFavourite">
<img src="/Content/images/star-on.png" title="Удалить из избранного" />
</a>
или
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#favourite" href="/Home/AddOrRemoveFavourite">
<img src="/Content/images/star-off.png" title="Добавить в избранное" />
</a>
Осталось добавить лишь div, картинки звездочек, вызов метода в представлении и сам метод AddOrRemoveFavourite, однако на последнем хотелось бы остановиться поподробнее.
4. Добавление метода AddOrRemoveFavourite в контроллер
В общем виде, метод, который обрабатывает асинхронный запрос, возвращает значение, будь то строка, число, JSON данные и пр., но в нашем случае, нам потребуется вернуть HTML разметку нового элемента, который генерирует наш расширяющий метод. Увы (или к счастью), в ASP .NET MVC 3 отсутствует возможность вызова нашего расширяющего метода на стороне сервера (в методе контроллера) и поэтому сгенерировать разметку без дублирования самого кода в методе контроллера у нас не получится (иначе, будет нарушен один из основополагающих принципов MVC- DRY).
Однако, из метода контроллера мы можем вернуть вызов Partial View, который инициирует вызов нашего расширяющего метода, который в свою очередь вернёт HTML разметку.
Приступим.
4.1. Добавляем Partial View с вызовом расширяющего метода
Добавляем в папку Views папку Partial. В Partial добавляем папку Favourites.
В папку Favourites добавляем View, выбирая галкой Create as partial view и указывая наименование AddOrRemoveFavourites_PartialView

В содержимое добавленного Partial View добавляем директивы:
@using MVC3_AjaxActionImageLink.Core
@Model bool
и сам вызов нашего метода:
@Ajax.ImageActionLink("AddOrRemoveFavourite", (bool)Model, new AjaxOptions() { UpdateTargetId = "favourite" });
Примечание. Для упрощения деталей моделью в данной статье будет являться статическая переменная типа bool.
Содержимое же Index.cshtml теперь поручает вызов расширяющего метода Partial View и выглядит следующим образом:
@using MVC3_AjaxActionImageLink.Core
@model bool
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<div id="favourite">
@{ Html.RenderPartial("/Views/Partial/Favourites/AddOrRemoveFavourites_PartialView.cshtml", Model); }
</div>
4.2. Добавляем метод обработки асинхронного запроса AddOrRemoveFavourite в контроллер и связываем с моделью
Для того, чтобы вернуть наш Partial View, требуется указать в return вызов метода PartialView, передав первым параметром путь. Кроме этого, так как мы сохраняем состояние примитивным способом через статическую bool переменную, мы передадим её как модель в обе наши View. Конечный вариант нашего класса HomeController.cs представлен ниже:
public class HomeController : Controller
{
public static bool inFavourite = true;
public ActionResult AddOrRemoveFavourite()
{
if (Request.IsAjaxRequest())
{
inFavourite = !inFavourite; // TODO: Add or remove favourite
return PartialView("/Views/Partial/Favourites/AddOrRemoveFavourites_PartialView.cshtml", inFavourite);
}
return View();
}
public ActionResult Index()
{
// TODO: Check, if row is already in favourites
return View(inFavourite);
}
}
5. Что осталось?
Завершающим этапом будет добавление папки images с картинками star-off.png и star-on.png, а так же подключение скрипта для работы с AJAX в ASP .NET MVC 3:
<script src="../../Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
Пробуем.

кликаем

Заключение

Итак, что же мы всё таки в данной статье сделали и усвоили:
- Рассмотрели возможность использования AJAX в ASP .NET MVC 3 с использованием метода Ajax.ActionLink
- Получили представление о создании собственных расширяющих методов и их использовании
- Узнали, как вернуть из AJAX запроса сгенерированную HTML разметку посредством вызова Html хелпера
- Не нарушили принцип DRY
Примечание
Наименование метода и принцип построения ссылки в виде изображения был взят из проекта Stephen Walthe's Contact manger project