Search
Write a publication
Pull to refresh

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

Данная статья посвящается созданию ссылки, содержащей другие 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);
}


Теперь можно использовать следующий код для рисования ссылки. Для использования необходимо прописать <b @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;
}


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

Спасибо за внимание.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.