29 декабря 2011 г.

Индикатор выполнения AJAX-запроса, в ASP.Net MVC

Сегодня практически каждая страница сайта выполняет какие-то AJAX-действия. Чаще всего эти запросы довольно "легкие", и не требуют длительного времени выполнения. Но иногда все-же встречаются ситуации, когда прежде чем выдать ответ, на сервере должен отработать немалый кусок логики с подключениями к БД или каким-либо сторонним ресурсам. В таких случаях пользователю нужно дать понять, что процесс пошел, и запрос находится на выполнении.

Итак, сегодня мы будем делать визуальную индикацию выполнения AJAX-запроса.

Рассмотрим реальный случай.
На странице у нас имеется кнопка, по нажатию на которую делается аджакс-запрос на сервер, где отработка кода занимает 2-3 секунды т.к. получается и обрабатывается большой кусок данных. Все происходит без перезагрузки страницы, тоесть нужно предпринять какие-то меры для того, чтобы в процессе ожидания пользователь не нажал на кнопку повторно, не зная, что запрос уже обрабатывается.

Сделаем следующее - после нажатия на кнопку запроса, дадим пользователю понять, что процесс запущен, и отберем у него возможность что-либо делать на сайте, пока не будет получен ответ с сервера.
Все очень просто. Перед выполнением AJAX-запроса мы покажем полупрозрачный div с индикатором выполнения. Этот div перекроет весь контент страницы, таким образом пользователь не сможет нажать кнопку повторно. А после завершения выполнения запроса, мы просто уберем див, вот и вся магия.

Начнем с того, что создадим сам div, который будет индикатором выполнения. Разумнее всего разместить его в Site.Master, сразу после открытия тэга body. Так он будет занимать все пространство страницы:
<body>
    <div id="loadingDiv" class="loader">
    </div>
    <div class="page">
........................

Здесь главное - это стиль. Я имею в виду css-стиль данного дива, который следует добавить в Site.css:
   1:  .loader
   2:  {
   3:      position: absolute;
   4:      z-index: 800;
   5:      background-image: url('/Content/Images/loading.gif');
   6:      background-repeat: no-repeat;
   7:      background-position: center center;
   8:      width: 100%;
   9:      height: 100%;
  10:      opacity: 0.6;
  11:      background-color: #CCCCCC;
  12:      display: none;
  13:  } 

Думаю здесь все понятно, но опишу некоторые моменты:
z-Index - для того, чтобы див находился поверх всех остальных элементов страницы;
opacity - прозрачность;
loading.gif - анимированная гифка с индикатором;
display: none - по-умолчанию, этот див всегда будет скрыт.

Теперь добавим на страницу простую кнопку:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <input type="button" value="Do AJAX Request" onclick="doRequest();" />
</asp:Content>

Создадим некий экшн контроллера, к которому будет происходить наш AJAX-запрос. В нем имитируем длительное выполнения с помощью приостановки текущего потока на 4 секунды:
   1:  [HttpPost]
   2:  public ActionResult SomeAction()
   3:  {
   4:      if (Request != null && Request.IsAjaxRequest())
   5:      {
   6:          System.Threading.Thread.Sleep(4000);
   7:   
   8:          return Content(bool.TrueString);
   9:      }
  10:   
  11:      return Content(bool.FalseString);
  12:  }

Осталось дело за Javascript-файлом, который подключим к проекту, и в котором будет реализовано все остальное:
   1:  function showLoader() {
   2:      $('#loadingDiv').show();
   3:  };
   4:   
   5:  function hideLoader() {
   6:      $('#loadingDiv').hide();
   7:  };
   8:   
   9:  function doRequest() {
  10:   
  11:      showLoader();
  12:   
  13:      $.ajax({
  14:          type: "POST",
  15:          url: "/Home/SomeAction",
  16:          success: function (result) {
  17:              if (result == 'True') {
  18:                  alert("Request is done!");
  19:              } else {
  20:                  alert("Request is fail!");
  21:              };
  22:          },
  23:          complete: function () {
  24:              hideLoader();
  25:          }
  26:      });
  27:  };

Как уже понятно, функции showLoader() и hideLoader() просто показывают/скрывают наш див-индикатор с помощью JQuery.
В функции doRequest(), перед непосредственным выполнением запроса, мы показываем индикатор выполнения, а убирается он лишь при срабатывании событие complete, для запроса. Может показаться, что можно разместить код скрытия индикатора в success. Конечно можно, но если в контроллере что-то пойдет не так, или адрес запроса будет недоступен, индикатор не исчезнет.
Тоесть success отрабатывает только при успешном выполнении запроса, а complete - отрабатывает в любом случае, после завершения запроса, даже если на каком-то этапе произошла ошибка.

На этом все. В результате, после нажатия на кнопку, и до принятия ответа сервера, пользователь будет видеть вот такую картину(только с вращающимся индикатором), и не сможет напакостить нам повторным нажатием на кнопку:

_____
Исходники