Интеграция пользовательского интерфейса jQuery и ASP.NET MVC

Дино Эспозито (Dino Esposito) | 5 мая 2010 г.

Библиотека пользовательских интерфейсов jQuery — отличное средство для построения динамических и высокочувствительных пользовательских интерфейсов. Написание простого представления для демонстрации ее возможностей — не слишком большое дело, и это определенно будет отличной инструкцией по самой библиотеке. Например, если вы подыскиваете компонент картотеки, часто запрашиваемую функцию, то пользовательский интерфейс jQuery предлагает решение на стороне клиента, позволяющее переключаться между вкладками без необходимости обновления всей страницы. Если контент вкладки не является статическим и доступен где-либо в текущей модели DOM, его можно загрузить из удаленного расположения, но автоматически отправляемый запрос использует классический объект XMLHttpRequest. Чтобы разместить каталог, требуется только создать специальную структуру HTML, а затем вызвать функцию запуска пользовательского интерфейса jQuery в корневом элементе дерева.

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

В этой статье я покажу, как изменять стандартный шаблон проекта приложений ASP.NET MVC 2, чтобы внедрять функциональные возможности пользовательского интерфейса jQuery. Окончательный результат представляет собой скелет приложения ASP.NET MVC, в интерфейсе которого реализуется каталог пользовательского интерфейса jQuery. Окончательный код — только отправная точка для приложения ASP.NET MVC с полной поддержкой технологии AJAX.

Установка проекта

Одним из правил для приложений ASP.NET MVC является хранение всех вспомогательных ресурсов, таких как CSS и изображения, в папке Content, и всех файлов скриптов — в папке Scripts. Эти правила можно изменить без воздействия на какие-либо аспекты поведения во время выполнения, но я не вижу причин для такого изменения. Поэтому давайте начнем с проекта ASP.NET MVC по умолчанию и предположим, что в папке Content имеется подкаталог со всеми файлами для выбранной темы пользовательского интерфейса jQuery. Также предположим, что все файлы скриптов пользовательского интерфейса jQuery размещены в папке Scripts. На рисунке 1 показан пример папки проекта.


Рисунок 1. Папка проекта после первого этапа

Кроме того, файл site.master будет связывать файлы скриптов для библиотеки. Далее приводится выдержка из главного файла, показывающая фактическое содержимое раздела <head>.

<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <link href="../../Content/Site.css" rel="stylesheet" rel="stylesheet" 
          type="text/css" />
    <link href="../../Content/jq-ui-Themes/sunny/jquery-ui-sunny.css" rel="stylesheet" 
          type="text/css" />  
    <script type="text/javascript" 
            src="../../Scripts/jquery-1.4.1.min.js" /> 
    <script type="text/javascript" 
            src="../../Scripts/jQueryUI/jquery-ui-1.7.2.custom.min.js" /> 
</head>

На этом предварительные действия по установке проекта заканчиваются. Библиотека пользовательских интерфейсов jQuery готова к использованию.

Главная страница

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


Рисунок 2. Главная страница по умолчанию приложений ASP.NET MVC.

Вкладки создаются с помощью следующего кода:

<div id="menucontainer">
    <ul id="menu">
       <li><%= Html.ActionLink("Home", "Index", "Home")%></li>
       <li><%= Html.ActionLink("About", "About", "Home")%></li>
    </ul>
</div>

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

<div id="menucontainer">
    <ul id="menu">
        <li><%= Html.ActionLink("Home", "Index", "Home", 
                     new { title="Home Page" }, null)%></li>
        <li><%= Html.ActionLink("About", "About", "Home", 
                     new { title = "About" }, null)%></li>
        <li><%= Html.ActionLink("Misc", "Demo", "Home", 
                     new { title = "Miscellaneous demos" }, null)%></li>
        <li><%= Html.ActionLink("What's up here", "Todo", "Home", 
                     new { title = "Read me" }, null)%></li>
    </ul>
</div>

Почему строка заголовка задана как аргумент?

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

Изменение ссылок действий во вкладках

Как уже упоминалось, блок DIV menucontainer, который можно найти в стандартной главной странице ASP.NET MVC, является прекрасной основой для каталога пользовательского интерфейса jQuery. Достаточно только добавить порцию кода скрипта, которая будет выполняться при загрузке каждой страницы на основе главной страницы. Можно воспользоваться возможностями jQuery (не возможностями пользовательского интерфейса jQuery) и зарегистрировать обработчик для события ready документа. Можно также использовать любые аналогичные возможности, которые предлагает ваша библиотека AJAX. В настоящее время особую популярность имеет jQuery, и все уверены, что в будущем эта популярность будет только расти. Поэтому приведем необходимый код jQuery:

<script type="text/javascript">
    $(document).ready(function() {
         $("menucontainer").tabs();
    });
</script>

На рисунке 3 показан новый вид страницы после оснащения ее пользовательским интерфейсом jQuery.


Рисунок 3. Каталог пользовательского интерфейса jQuery вместо статических вкладок.

Два момента следует особо упомянуть и объяснить более глубоко. Первый связан с домашней страницей сайта, а второй — с заголовком страницы и его изменением при перемещении пользователя по вкладкам.

Шаблон default.aspx

В обычном приложении ASP.NET MVC файл default.aspx не требуется. Единственное исключение — когда приложение ASP.NET MVC размещается в IIS 6 или IIS 7, но настроено для выполнения в классическом режиме (в отличие от режима конвейера). В обычных обстоятельствах домашняя страница приложения ASP.NET MVC является результатом объединения главной страницы с шаблоном Index.aspx. Кроме того, метод Index контроллера Home является действием по умолчанию, определяющим, что видит пользователь при первом посещении сайта. Что больше всего нравится в этом шаблоне default.aspx?

При включении сайта ASP.NET MVC с помощью пользовательского интерфейса jQuery потребуется дополнительная страница-контейнер, в которой будет вставлен блок DIV и весь соответствующий код скрипта для каталога. Метод Index контроллера Home или какого-либо действия, которое решено вызывать для заполнения домашней страницы, теперь отвечает не за всю домашнюю страницу, а только за контент, который отправляется во вкладку. Если сказать более конкретно, это означает, что для содержания вкладки требуется дополнительный шаблон и действие контроллера. Я решил вызвать метод Default, и принимая во внимание следующий код, основным представлением является default.aspx. Оно появляется без сообщения, что вы можете изменить его имя в любое удобное время. Реализация метода Default, как и структуры представления default.aspx, не является слишком сложной. Вот как она выглядит:

public class HomeController : Controller
{
    public ActionResult Default()
    {
        return View();
    }
    :
}

Аналогично, разметка в представлении — довольно тривиальная вещь:

<%@ Page Language="C#" 
         MasterPageFile="~/Views/Shared/Site.Master" 
         Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
    Nice UI
</asp:Content>

Наконец, необходимо также внести некоторые изменения в файл global.asax, в частности, в разделе, где задаются и регистрируются поддерживаемые маршруты:

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { 
                  controller = "Home", 
                  action = "Default", 
                  id = UrlParameter.Optional 
            }    
        );
    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }
}

Как можно видеть, теперь действием по умолчанию является Default вместо стандартного метода Index. Это означает, что роль и контент представлений, связанных из каталога, отличаются от стандартного приложения ASP.NET MVC.

Давайте взглянем на код метода Index и соответствующего представления:

public ActionResult Index()
{
    ViewData["Message"] = "Welcome to the <span id=\"hiTitle\">
                           MVC+jQueryUI</span> template!";
    return View();
}

Как можно видеть, нет ничего особенного в методе Index и в любом другом методе, связанном из каталога. Ключевое различие находится в представлении Index.aspx:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<script type="text/javascript">
    $(document).ready(function() {
       :
    });
</script>
<div>
    <table>
        <tr>
            <td>
                <h2><%= ViewData["Message"] %></h2>
                <p>
                    To learn more about ASP.NET MVC visit 
                    <a href="http://asp.net/mvc" 
                       title="ASP.NET MVC Website"> http://asp.net/mvc </a>.
                </p>
                <p>
                    To learn more about jQuery UI visit 
                    <a href="http://jqueryui.com" 
                       title="jQuery UI Website"> http://jqueryui.com </a>.
                </p>
            </td>
            <td valign="top">
               :
            </td>
        </tr>
    </table>
</div>

Необработанный контент почти такой же, что и в аналогичном представлении из стандартного шаблона проекта. Огромное различие в том, что при введении в действие пользовательского интерфейса jQuery и наличии нового представления default.aspx Index и все другие представления, связанные из каталога, являются частичными представлениями. В результате они не строят всю страницу и не нуждаются в наследовании от главной страницы. Представление Index просто возвращает кусок HTML, который объединяется в шаблоне представления default.aspx.

Вкладка Home, которую можно видеть на рисунке 3, связана с URL-адресом /Home/Index, и аналогичный URL-адрес ASP.NET MVC подключается ко всем вкладкам. Когда пользователь выбирает вкладку, компонент каталога пользовательского интерфейса jQuery подключается к URL-адресу с помощью технологии AJAX и загружает ответ. Затем ответ, как он есть, вставляется в текущую модель DOM в поддереве, представляющем контент нажатой вкладки. По этой точной причине URL-адрес не может возвращать полное представление страницы, и должен быть ограничен возвратом частичного представления. В терминах программирования ASP.NET MVC это просто означает, что придется настроить представление таким образом, чтобы оно возвращало порцию HTML. Это можно получить, просто удалив зависимости в главной странице. Того же результата можно добиться, написав пользовательский элемент управления и возвратив его с помощью PartialView из метода, как показано далее.

public ActionResult Index()
{
    :
    return PartialView();  // предполагается index.ascx в папке Views
}

В итоге при переключении в шаблон пользовательского интерфейса jQuery необходимо предполагать базовое представление, которое является классической страницей ASP.NET MVC, и по одному частичному представлению для каждой вкладки. Разметка для вкладки автоматически загружается с помощью технологии AJAX, и контент кэшируется, как любой другой веб-ресурс.

Обратите внимание, что при запросе /Home/Index в строке адреса в этом шаблоне пользовательского интерфейса jQuery появится либо ошибка сценария, либо скудная черно-белая страница HTML, в зависимости от установок пользователя для обработки скриптов в браузере.

Изменение заголовка страницы

В классическом ASP.NET MVC, если вкладки являются простыми ссылками для перехода на другие страницы, каждое представление может легко установить заголовок страницы. Когда пользователь переходит с вкладки Home на вкладку About, представление About довольно просто устанавливает заголовок страницы с помощью любых доступных средств — порции кода скрипта, прототипа из главной страницы и даже API, предлагаемого классом ViewPage программной части.

Как установить заголовок страницы, когда URL-адреса Home/Index (и аналогичные) являются только конечными точками для частичных представлений? В таком случае следует полагаться на API каталога и перехватывать щелчок пользователя, изменяющий выбор вкладки. Поскольку этот код скрипта является общим для всех страниц на основе пользовательского интерфейса jQuery, можно вставить его в главную страницу или в базовую страницу default.aspx. Далее приводится необходимый код.

<script type="text/javascript">
    $(document).ready(function() {
         $("menucontainer").tabs();
         $("menucontainer").bind( 'tabsselect', 
             function( event, ui ) {
                document.title = ui.tab.title;  
             }
         );
    });
</script>

Как ранее было сказано, первый вызов только устанавливает каталог. Второй вызов регистрирует обработчик событий для события tabsSelect, которое вызывается каталогом пользовательского интерфейса jQuery. Событие вызывается всякий раз при изменении пользователем выбора вкладки. В этом случае код для выполнения специфичен для параметра анонимной функции. Можно видеть, что он только устанавливает заголовок конкретного документа, страницы, в значение свойства title выбранной в текущий момент вкладки. Выражение ui.tab используется для ссылки на корень поддерева модели DOM, представляющего эту вкладку. Значение, которое назначается атрибуту title, совпадает со значением, переданным в качестве атрибута title при создании ссылки для вкладки. Другими словами, одна и та же вкладка используется как для подсказки вкладки, так и для заголовка страницы, отображаемой при выборе вкладки.

Выводы

С развитием библиотеки jQuery и полным принятием Майкрософт, библиотека пользовательских интерфейсов jQuery также будет неизбежно все больше и больше использоваться. ASP.NET MVC представляет новую грань разработки ASP.NET, и она убедительно характеризуется предоставляемой степенью контроля над разметкой. Контроль над разметкой означает контроль каждой порции HTML, но также и контроль всего кода скрипта, направляемого в браузер. По этой причине объединение ASP.NET MVC и jQuery уже приветствуется как мощный дуэт. Библиотека пользовательских интерфейсов jQuery только добавляет дополнительные специи в уже вкусное блюдо.