Параметры Ajax

98

Поведение запросов Ajax можно настроить, устанавливая значения свойств объекта AjaxOptions, который передается вспомогательному методу Ajax.BeginForm(). Мы рассмотрели использование этого метода в предыдущей статье, в последующих разделах мы объясним действие и назначение параметров AjaxOptions.

Обеспечение плавного сокращения возможностей

При создании формы с поддержкой Ajax в примере из предыдущей статьи, мы передавали имя метода действия, который должен был вызываться асинхронно. В рассмотренном примере это было действие GetPeopleData, которое генерирует частичное представление с фрагментом HTML-разметки.

Однако проблема такого подхода заключается в том, что он не очень хорошо работает, если пользователь отключил поддержку JavaScript (или эта поддержка вообще отсутствует в браузере). В таких случаях, когда пользователь отправляет форму, браузер отбрасывает текущую HTML-страницу и заменяет ее фрагментом, возвращенным целевым методом действия. Этот эффект можно наблюдать на рисунке ниже:

Эффект от использования Ajax в браузере без поддержки JavaScript

На рисунке приведен экранный снимок окна браузера Google Chrome, т.к. он позволяет легко включать и отключать поддержку JavaScript.

Простейший способ решения этой проблемы предусматривает указание в свойстве AjaxOptions.Url целевого URL для асинхронного запроса вместо передачи имени действия в качестве аргумента методу Ajax.BeginForm(), как показано в примере ниже:

@using HelperMethods.Models
@model string
@{
    ViewBag.Title = "Данные пользователей";
    AjaxOptions ajaxOptions = new AjaxOptions
    {
        UpdateTargetId = "tableBody",
        Url = Url.Action("GetPeopleData")
    };
}

...

@using (Ajax.BeginForm(ajaxOptions))
{
    <div>
        @Html.DropDownList("selectedRole", new SelectList(
            new[] { "All" }.Concat(Enum.GetNames(typeof(Role)))))
        <button type="submit">Отобразить</button>
    </div>
}

Мы применяем вспомогательный метод Url.Action() для создания URL, который будет вызывать действие GetPeopleData, и используем версию метода Ajax.BeginForm(), принимающую единственный параметр AjaxOptions. В результате создается элемент <form>, который осуществляет обратную отправку инициировавшему методу действия, если поддержка JavaScript не включена:

...
<form action="/People/GetPeople" id="form0" method="post" 
    data-ajax="true" 
    data-ajax-mode="replace" 
    data-ajax-update="#tableBody" 
    data-ajax-url="/People/GetPeopleData" >
...

Если поддержка JavaScript включена, то библиотека ненавязчивого Ajax получит целевой URL из атрибута data-ajax-url, который ссылается на наше дочернее действие. Если же поддержка JavaScript отключена, тогда браузер будет использовать обычный прием отправки формы, получающий целевой URL из атрибута action, который указывает на метод действия, генерирующий полную HTML-страницу.

Вас может удивить, зачем так сильно заботиться о пользователях, отключивших поддержку JavaScript. В конце концов, кто так поступает? На самом деле это на удивление широко распространено в двух группах пользователей:

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

Предоставление пользователю обратной связи при выполнении запроса Ajax

Один из недостатков применения Ajax связан с тем, что пользователю не вполне понятно, происходит что-нибудь или нет, т.к. запрос к серверу выполняется в фоновом режиме. Проинформировать пользователя о том, что запрос находится в состоянии выполнения, можно с помощью свойств AjaxOptions.LoadingElementId и AjaxOptions.LoadingElementDuration. В примере ниже демонстрируется применение этих свойств в файле представления GetPeople.cshtml:

@using HelperMethods.Models
@model string
@{
    ViewBag.Title = "Данные пользователей";
    AjaxOptions ajaxOptions = new AjaxOptions
    {
        UpdateTargetId = "tableBody",
        Url = Url.Action("GetPeopleData"),
        LoadingElementDuration = 1000,
        LoadingElementId = "loading"
    };
}

<h2>Данные пользователей</h2>

<div id="loading" class="load" style="display:none">
    <p>Загрузка данных...</p>
</div>

<table>
    ...
</table>

...

Свойство AjaxOptions.LoadingElementId указывает значение атрибута id скрытого HTML-элемента, который будет показан пользователю на время выполнения запроса Ajax. Для демонстрации этой функциональной возможности мы добавили в представление элемент div, скрыв его от пользователя путем установки CSS-свойства display в none. Атрибут id этого элемента <div> устанавливается в loading, кроме того, loading указывается в качестве значения для свойства LoadingElementId.

Средство ненавязчивого Ajax будет отображать упомянутый элемент <div> пользователю на протяжении выполнения запроса:

Предоставление пользователю обратной связи во время выполнения запроса Ajax

В свойстве LoadingElementDuration указывается продолжительность анимации, которая применяется для постепенного отображения элемента loading пользователю. В коде задано значение 1000, что соответствует одной секунде.

Выдача пользователю вопроса перед запросом

Свойство AjaxOptions.Confirm позволяет указать сообщение, которое будет применяться при выдаче пользователю вопроса перед каждым асинхронным запросом. Пользователь может выбрать продолжение работы или отмену запроса. В примере ниже показано, как это свойство применяется в файле GetPeople.cshtml:

@using HelperMethods.Models
@model string
@{
    ViewBag.Title = "Данные пользователей";
    AjaxOptions ajaxOptions = new AjaxOptions
    {
        UpdateTargetId = "tableBody",
        Url = Url.Action("GetPeopleData"),
        LoadingElementDuration = 1000,
        LoadingElementId = "loading",
        Confirm = "Вы действительно хотите получить новые данные?"
    };
}
...

Благодаря такому добавлению, пользователю выдается вопрос каждый раз, когда он отправляет форму:

Выдача вопроса пользователю перед выполнением запроса

Такой вопрос будет задаваться перед каждым запросом, а это значит, что применять такое средство следует не особенно часто, чтобы не надоедать пользователю.

Создание ссылок Ajax

В дополнение к формам, ненавязчивый Ajax можно применять для создания элементов <a>, по которым допускается переходить асинхронным образом. Механизм очень похож на способ работы форм Ajax. В примере ниже показано, как добавить ссылки Ajax в представление GetPeople.cshtml:

...

@using (Ajax.BeginForm(ajaxOptions))
{
    ...
}

<div>
    @foreach (string role in Enum.GetNames(typeof(Role)))
    {
        <div class="ajaxLink">
            @Ajax.ActionLink(role, "GetPeopleData",
                new { selectedRole = role },
                new AjaxOptions
                {
                    UpdateTargetId = "tableBody"
                })
        </div>
    }
</div>

Мы добавили цикл foreach, в котором вспомогательный метод Ajax.ActionLink() вызывается для каждого значения, определенного перечислением Role, и создает элементы <a>, поддерживающие Ajax. Сгенерированные элементы <a> имеют тот же вид атрибутов data, как и при работе с формами.

В конфигурации маршрутизации для проекта не предусмотрено записи для переменной selectedRole, поэтому URL, сгенерированный для атрибута href, указывает роль, которую представляет ссылка, используя компонент строки запроса URL. Добавленные в представление ссылки показаны на рисунке ниже:

Добавление к представлению ссылок, поддерживающих Ajax

Щелчок на одной из этих ссылок приводит к вызову метода действия GetUserData() и замене содержимого элемента <tbody> возвращаемым им фрагментом HTML-разметки. Это дает тот же самый эффект фильтрации данных, что и достигнутый ранее с помощью формы, поддерживающей Ajax. Чтобы увидеть изменения, внесенные в этот пример, может потребоваться очистка хронологии браузера.

Обеспечение плавного сокращения возможностей для ссылок

Со ссылками Ajax связана та же проблема, что и с формами. Если в браузере отсутствует или отключена поддержка JavaScript, то щелчок на такой ссылке приводит просто к отображению фрагмента HTML-разметки, сгенерированного методом действия GetPeopleData().

Эту проблему можно решить путем применения свойства AjaxOptions.Url для указания URL с запросом Ajax и передачи действия GetPeople вспомогательному методу Ajax.ActionLink(), как показано в примере ниже:

...
<div>
    @foreach (string role in Enum.GetNames(typeof(Role)))
    {
        <div class="ajaxLink">
            @Ajax.ActionLink(role, "GetPeople",
                new { selectedRole = role },
                new AjaxOptions
                {
                    UpdateTargetId = "tableBody",
                    Url = Url.Action("GetPeopleData", new { selectedRole = role })
                })
        </div>
    }
</div>
...

Именно по этой причине для каждой требуемой ссылки создается новый объект AjaxOptions, а не используется один объект, созданный в блоке кода Razor для элемента <form>. Независимые объекты AjaxOptions позволяют указывать разные значения для свойства Url в каждой ссылке и поддерживают плавное сокращение возможностей для браузеров с отключенной поддержкой JavaScript.

Работа с обратными вызовами Ajax

В классе AjaxOptions определен набор свойств, позволяющих указывать JavaScript-функции, которые будут вызываться в различных точках жизненного цикла запроса Ajax. Эти свойства описаны в таблице ниже:

Свойство Событие jQuery Описание
OnBegin beforeSend

Вызывается непосредственно перед отправкой запроса

OnComplete complete

Вызывается, когда запрос завершился, независимо оттого, успешно или неудачно, но перед обновлением страницы

OnFailure error

Вызывается в случае, если запрос завершился неудачно

OnSuccess success

Вызывается в случае, если запрос завершился успешно

Каждому свойству обратного вызова AjaxOptions соответствует событие Ajax, поддерживаемое библиотекой jQuery. События jQuery были перечислены в таблице для тех читателей, которые использовали jQuery ранее.

В примере ниже демонстрируется применение элемента <script> для определения ряда базовых JavaScript-функций, которые будут сообщать о продвижении запроса Ajax, а также использование свойств из таблицы выше для указания этих функций в качестве обработчиков событий Ajax:

@using HelperMethods.Models
@model string
@{
    ViewBag.Title = "Данные пользователей";
    AjaxOptions ajaxOptions = new AjaxOptions
    {
        UpdateTargetId = "tableBody",
        Url = Url.Action("GetPeopleData"),
        LoadingElementDuration = 1000,
        LoadingElementId = "loading",
        Confirm = "Вы действительно хотите получить новые данные?"
    };
}

<script type="text/javascript">
    function OnBegin() {
        console.log('Это сообщение получено из обработчика onBegin')
    }
    function OnSuccess(data) {
        console.log('Это сообщение получено из обработчика onSuccess: ' + data)
    }
    function OnFailure(request, error) {
        console.log('Это сообщение получено из обработчика onFailure: ' + error)
    }
    function OnComplete(request, status) {
        console.log('Это сообщение получено из обработчика onComplete: ' + status)
    }
</script>

...

<div>
    @foreach (string role in Enum.GetNames(typeof(Role)))
    {
        <div class="ajaxLink">
            @Ajax.ActionLink(role, "GetPeople",
                new { selectedRole = role },
                new AjaxOptions
                {
                    UpdateTargetId = "tableBody",
                    Url = Url.Action("GetPeopleData", new { selectedRole = role }),
                    OnBegin = "OnBegin",
                    OnSuccess = "OnSuccess",
                    OnComplete = "OnComplete",
                    OnFailure = "OnFailure"
                })
        </div>
    }
</div>

Было определено четыре функции, по одной для каждого обратного вызова. В этом примере для простоты в каждой функции мы просто отображаем сообщение в консоли браузера. Благодаря таким изменениям, щелчок на одной из ссылок приводит к отображению последовательности сообщений о продвижении запроса Ajax:

Последовательность сообщений, отображаемых в ответ на обратные вызовы Ajax

Такие JavaScript-функции можно использовать для любых целей: манипуляция DOM-моделью HTML, запуск дополнительных запросов и т.д. Одной из наиболее полезных задач, которые можно решать посредством обратных вызовов Ajax, является обработка данных JSON, которая рассматривается в следующей статье.

Пройди тесты
Лучший чат для C# программистов