ASP .NET MVC 3. Как сделать, чтобы Ajax.ActionLink содержал html разметку

29 Дек
2011

Данная статья посвящается созданию ссылки, содержащей другие HTML тэги внутри.

Наверное многие встречались с проблемой, как сделать так, чтобы была ajax ссылка, содержащая другие html элементы (картинки, текст и прочее).

Прочитав данную статью, вы сможете реализовать данное без особых усилий.

Будем создавать некий аналог всем известного метода Ajax.ActionLink, а точнее Extension метод для AjaxHelperAjax.BeginActionLink.

Подробнее прочитать что такое AjaxHelper или ActionLink можно здесь.

Итак начнем

1. Для начала нам нужно создать класс MvcAnchor

public class MvcAnchor : IDisposable {
public MvcAnchor(HttpResponseBase httpResponse) {
if (httpResponse == null) {
throw new ArgumentNullException("httpResponse");
}
this._writer = httpResponse.Output;
}

public MvcAnchor(ViewContext viewContext) {
if (viewContext == null) {
throw new ArgumentNullException("viewContext");
}
this._writer = viewContext.Writer;
}

private bool _disposed;
private readonly TextWriter _writer;

#region IDisposable Members

public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}

public void Dispose(bool isDispose) {
if (!this._disposed) {
this._disposed = true;
this._writer.Write("</a>");
}
}

public void EndAnchor() {
Dispose(true);
}

#endregion


Данный класс нам необходим для того, чтобы закрывать тело ссылки, используя конструкцию using.

Теперь приступим к самой реализации extension метода.

Для создания extension метода нам понадобиться статический класс AjaxUtility
namespace MVC.Web.Helper {
public static class AjaxUtility {

}
}


BeginActionLink содержит следующие параметры:
  • string actionName — название серверного Action метода контроллера, куда поступит запрос на обработку;
  • string controllerName — название контроллера, куда поступит запрос в поисках Action метода для обработки;
  • string protocol — протокол, по которому будет отправлен запрос (например «http» или «https»);
  • string hostName — доменное имя (например «google.com»);
  • string fragment — наименование фрагмента, которое добавится после # в конец URL’a;
  • RouteValueDictionary / object routeValues — параметры, передаваемые на сервер в строке URL’a;
  • AjaxOptions ajaxOptions — опции выполнения асинхронного запроса
  • IDictionary<string, Object> htmlAttributes — коллекция ключ-значение, в которой можно указать добавляемые к сгенерированной HTML разметке аттрибуты (например new { @class = «link» }).


Создаем метод BeginActionLink:
public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string, object> htmlAttributes)


Для того, чтобы получить адрес ссылки, который мы вставим в аттрибут href у ссылки воспользуемся классом UrlHelper.

string targetUrl = UrlHelper.GenerateUrl(null, actionName, controllerName, protocol, hostName, fragment, routeValues, ajaxHelper.RouteCollection, ajaxHelper.ViewContext.RequestContext, true);


Далее начнем построение элемента ссылки, используя класс TagBuilder.
 TagBuilder builder = new TagBuilder("a");
builder.MergeAttributes<string, object>(htmlAttributes);
builder.MergeAttribute("href", targetUrl);
builder.MergeAttributes<string, object>(options.ToUnobtrusiveHtmlAttributes());
ajaxHelper.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag));
return new MvcAnchor(ajaxHelper.ViewContext);


Строка builder.MergeAttributes<string, object>(options.ToUnobtrusiveHtmlAttributes()); как раз и добавляет unobtrusive html аттрибуты, которые будут работать при подключении скрипта jquery.unobtrusive-ajax

Строка ajaxHelper.ViewContext.Writer.Write(builder.ToString(TagRenderMode.StartTag)); записывает в тело ответа тег «а», который получился, не закрывая его.

Возвращаем экземпляр класса MvcAnchor
return new MvcAnchor(ajaxHelper.ViewContext);

Внутрь передается контекст представления для того, чтобы при вызове метода Dispose можно было дописать закрытие тега «a (
</a>
)».

Далее создавая методы перегрузки можно получить аналог ActionLink
public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, AjaxOptions ajaxOptions) {
return ajaxHelper.BeginActionLink(actionName, null, ajaxOptions);
}

public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, string controllerName, AjaxOptions ajaxOptions, object routeValues, object htmlAttributes) {
return ajaxHelper.BeginActionLink(actionName, controllerName, ajaxOptions, new RouteValueDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}

public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, AjaxOptions ajaxOptions, object routeValues, object htmlAttributes) {
return ajaxHelper.BeginActionLink(actionName, null, ajaxOptions, new RouteValueDictionary(routeValues), HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
}

public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, string controller, AjaxOptions ajaxOptions) {
return ajaxHelper.BeginActionLink(actionName, controller, ajaxOptions, null);
}

public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, AjaxOptions ajaxOptions, object routeValues) {
return ajaxHelper.BeginActionLink(actionName, null, ajaxOptions, new RouteValueDictionary(routeValues), null);
}

public static MvcAnchor BeginActionLink(this AjaxHelper ajaxHelper, string actionName, string controllerName, AjaxOptions ajaxOptions, object routeValues) {
return ajaxHelper.BeginActionLink(actionName, controllerName, ajaxOptions, new RouteValueDictionary(routeValues), null);
}


Теперь можно использовать следующий код для рисования ссылки. Для использования необходимо прописать @using MVC.Web.Helper в заголовке View, чтобы extention метод заработал или зарегистрировать данный namespace в Web.config, находящийся в папке Views в раздел <system.web.webPages.razor>/<pages>/<namespaces>
@using(Ajax.BeginActionLink("Index", "Home", null, new AjaxOptions { UpdateTargetId = "main"}, new { @class="link"})) {
<img src="/images/logo.jpg" />
<span class="text">Домой</span>
}

В результате мы получим следующий результат:
<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#main" href="/Home/Index">
<img src="/images/logo.jpg" />
<span class="text">Домой</span>
</a>


Для тех, у кого в настройках web.config стоит опция <add key=«UnobtrusiveJavaScriptEnabled» value=«true» /> могут наслаждаться результатом.

Сейчас расскажу, каким модифицировать метод для режима UnobtrusiveJavaScriptEnabled=false.

К сожалению, Microsoft скрыли реализацию методов, необходимых для построения скрипта AJAX запроса на основе MicrosoftMvcAjax.js.

Придется сделать это самому следующим образом:

1. Добавим условие для нашего метода, для этого заменим строку builder.MergeAttributes<string, object>(options.ToUnobtrusiveHtmlAttributes()); на следующий фрагмент кода:
if (ajaxHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
builder.MergeAttributes<string, object>(options.ToUnobtrusiveHtmlAttributes());
else
builder.MergeAttribute("onclick", GenerateAjaxScript(options, "Sys.Mvc.AsyncHyperlink.handleClick(this, new Sys.UI.DomEvent(event), {0});"));


2. Создадим метод GenerateAjaxScript:
private static string GenerateAjaxScript(AjaxOptions ajaxOptions, string scriptFormat) {
string str = ajaxOptions.ToJavascriptString();
return string.Format(CultureInfo.InvariantCulture, scriptFormat, new object[] { str });
}


3. Созданим extention метод для AjaxOptions, который создает объект с настройками в javascript
public static string ToJavascriptString(this AjaxOptions ajaxOptions) {
var builder = new StringBuilder("{");
builder.Append(string.Format(CultureInfo.InvariantCulture, " insertionMode: {0},", new object[] { InsertionModeString(ajaxOptions) }));
builder.Append(PropertyStringIfSpecified("confirm", ajaxOptions.Confirm));
builder.Append(PropertyStringIfSpecified("httpMethod", ajaxOptions.HttpMethod));
builder.Append(PropertyStringIfSpecified("loadingElementId", ajaxOptions.LoadingElementId));
builder.Append(PropertyStringIfSpecified("updateTargetId", ajaxOptions.UpdateTargetId));
builder.Append(PropertyStringIfSpecified("url", ajaxOptions.Url));
builder.Append(EventStringIfSpecified("onBegin", ajaxOptions.OnBegin));
builder.Append(EventStringIfSpecified("onComplete", ajaxOptions.OnComplete));
builder.Append(EventStringIfSpecified("onFailure", ajaxOptions.OnFailure));
builder.Append(EventStringIfSpecified("onSuccess", ajaxOptions.OnSuccess));
builder.Length--;
builder.Append(" }");
return builder.ToString();
}
private static string InsertionModeString(AjaxOptions options) {
switch (options.InsertionMode) {
case InsertionMode.Replace:
return "Sys.Mvc.InsertionMode.replace";
case InsertionMode.InsertBefore:
return "Sys.Mvc.InsertionMode.insertBefore";
case InsertionMode.InsertAfter:
return "Sys.Mvc.InsertionMode.insertAfter";
}
return ((int)options.InsertionMode).ToString(CultureInfo.InvariantCulture);
}

private static string EventStringIfSpecified(string propertyName, string handler) {
if (!string.IsNullOrEmpty(handler)) {
return string.Format(CultureInfo.InvariantCulture, " {0}: Function.createDelegate(this, {1}),", new object[] { propertyName, handler.ToString() });
}
return string.Empty;
}

private static string PropertyStringIfSpecified(string propertyName, string propertyValue) {
if (!string.IsNullOrEmpty(propertyValue)) {
string str = propertyValue.Replace("'", @"\'");
return string.Format(CultureInfo.InvariantCulture, " {0}: '{1}',", new object[] { propertyName, str });
}
return string.Empty;
}


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

Спасибо за внимание.
По материалам Хабрахабр.



загрузка...

Комментарии:

Наверх