Взаимодействие с JavaScript

85

В этой и последующих статьях рассматриваются способы взаимодействия JavaScript и ASP.NET: создание сложных сценариев, построение интерактивных серверных элементов управления ASP.NET, асинхронное взаимодействие с сервером с помощью Ajax и т.д. Если вы не знакомы с основами JavaScript, рекомендую обратится к разделу «Учебник JavaScript» на нашем сайте.

Создание JavaScript-процессора страницы

Сколько раз вам приходилось щелкать на веб-странице и затем наблюдать за загрузкой сайта в строке состояния вашего браузера? Возможно, разорвано соединение с Интернетом? Или произошла какая-то ошибка подключения к базовой системе? Или же просто система работает столь медленно? Часто эти проблемы усложняют признание новых веб-решений, особенно при замене более быстро реагирующего и предоставляющего большие возможности клиентского приложения (такого как Windows-приложение).

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

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

Проблему задержки обработки невозможно решить, добавляя код JavaScript в целевую страницу, поскольку этот код не будет запущен до завершения обработки страницы и возврата визуализированной HTML-разметки пользователю. Однако универсальный процессор страницы, который обрабатывает запросы к любой отнимающей много времени страницы, можно создать на своем сайте.

Чтобы создать процессор страницы, понадобится отреагировать на JS-события onload и onunload. Ниже приведена страница (по имени PageProcessor.aspx), в которой демонстрируется этот подход. Она отображает индикатор загрузки страницы (большую коллекцию индикаторов можно загрузить на сайте www.ajaxload.info). Элемент <body> связан с двумя функциями, которые вскоре будут рассмотрены.

<html>
<head runat="server">
    <title></title>
    <script>
        /* Здесь располагаются функции JavaScript */
    </script>
</head>
<body onload="BeginPageLoad();" onunload="EndPageLoad();">
    <form id="form1" runat="server">
        <div style="position:absolute; left:50%; top:50%; margin-left:-110px; margin-top:-25px">
            <span id="MessageText" style="font-size: x-large; font-weight: bold">Загрузка страницы</span>
            <span id="ProgressMeter"></span>
            <br />
            <img src="ajax-loader.gif" />
        </div>
    </form>
</body>
</html>

Чтобы использовать процессор страницы, нужно запросить эту страницу и передать в аргументе строки запроса требуемую страницу. Например, для загрузки в фоновом режиме страницы Default.aspx понадобилось бы использовать следующий URL-адрес:

PageProcessor.aspx?Page=Default.aspx

Процессор страницы нуждается в очень небольшом объеме серверного кода. Фактически, все его действия сводятся к извлечению первоначально запрошенной страницы из строки запроса и ее сохранению в защищенной переменной класса страницы. (Это полезно, поскольку затем эту переменную можно предоставить коду JavaScript с использованием выражения привязки данных ASP.NET, как будет показано чуть позже.) Полный серверный код станицы PageProcessor.aspx имеет следующий вид:

public partial class PageProcessor : System.Web.UI.Page
{
    protected string PageToLoad;

    protected void Page_Load(object sender, System.EventArgs e)
    {
        PageToLoad = Request.QueryString["Page"];
    }
}

Остальная часть работы выполняется клиентским кодом JavaScript. При первой загрузке процессора страницы генерируется событие onload, которое вызывает функцию BeginPageLoad() клиентской стороны. Функция BeginPageLoad() сохраняет текущее окно открытым и начинает извлечение страницы, запрошенной пользователем. Для этого применяется метод window.setInterval(), устанавливающий таймер, который периодически вызывает специальную функцию UpdateProgressMeter().

Код JavaScript-функции BeginPageLoad() выглядит следующим образом:

var iLoopCounter = 1;
var iMaxLoop = 6;
var iIntervalId;

function BeginPageLoad() {
    // Перенаправить браузер на другую страницу с сохранением фокуса
    location.href = "<%=PageToLoad %>";

    // Обновлять индикатор выполнения каждые 0.5 секунды
    iIntervalId = window.setInterval(
        "iLoopCounter=UpdateProgressMeter(iLoopCounter,iMaxLoop);", 500);
}

Первый оператор кода указывает на страницу по ее новому URL-адресу. Обратите внимание, что страница, которую требуется загрузить, не является жестко закодированной в коде JavaScript. Вместо этого она устанавливается с помощью выражения привязки данных <%=PageToLoad %>. Когда страница визуализируется на сервере, ASP.NET автоматически вставляет значение переменной PageToLoad на свое место.

Последний оператор кода запускает таймер, используя функцию window.setInterval(). Каждые 500 миллисекунд этот таймер срабатывает, и указанная строка кода выполняется. Эта строка кода вызывает JavaScript-функцию по имени UpdateProgressMeter() и продолжает отслеживать текущий счетчик цикла.

Функция UpdateProgressMeter() просто периодически изменяет сообщение о состоянии, придавая ему сходство с анимированным индикатором выполнения. Сообщения о состоянии циклически повторяется, периодами от 0 до 5. Код JavaScript, который решает эту задачу, имеет следующий вид:

function UpdateProgressMeter(iCurrentLoopCounter, iMaximumLoops) {
    // Найти объект для элемента span с текстом о состоянии выполнения
    var progressMeter = document.getElementById("ProgressMeter")

    iCurrentLoopCounter += 1;
    if (iCurrentLoopCounter <= iMaximumLoops) {
        progressMeter.innerHTML += ".";
        return iCurrentLoopCounter;
    }
    else {
        progressMeter.innerHTML = "";
        return 1;
    }
}

И, наконец, когда страница полностью загружена, генерируется клиентское событие onunload. В приведенном примере событие onunload связано с функцией EndPageLoad(). Эта функция останавливает таймер, очищает сообщение о ходе выполнения и устанавливает временное сообщение передачи, которое исчезает, как только новая страница визуализируется в браузере. Ниже приведен код:

function EndPageLoad() {
    window.clearInterval(iIntervalId);

    var progressMeter = document.getElementById("ProgressMeter")
    progressMeter.innerHTML = "Страница загружена - переадресация";
}

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

Автоматизированный индикатор выполнения

Чтобы протестировать процессор страницы, нужно просто воспользоваться целевой страницей, которая занимает много времени при выполнении на сервере (из-за объема работ, выполняемых кодом) или при загрузке на клиенте (из-за размеров страницы). Медленную страницу можно имитировать, помещая в целевую страницу код задержки, подобный следующему:

protected void Page_Load(object sender, EventArgs e)
{
        // Имитировать медленную загрузку страницы (пятисекундное ожидание)
        System.Threading.Thread.Sleep(5000);
}

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

Как видите, совсем небольшой объем клиентского кода JavaScript позволяет информировать пользователя о том, что страница обрабатывается. А это, в свою очередь, улучшает субъективное представление пользователя о производительности.

Визуализация блоков сценария

В рассмотренном ранее примере использовался статический блок <script>, который вставлялся непосредственно в .aspx-часть страницы.

Однако часто более гибким методом визуализации сценария является применение свойства Page.ClientScript, которое предоставляет объект ClientScriptManager с несколькими полезными методами управления блоками сценария. Ниже описаны два наиболее полезных из них:

RegisterClientScriptBlock()

Записывает блок сценария в начало веб-формы, непосредственно после дескриптора <form runat="server">.

RegisterStartupScript()

Записывает блок сценария в конец веб-формы, непосредственно перед дескриптором </form>

Эти два метода решают одну и ту же задачу - принимают строковый ввод с блоком <script> и добавляют его к сгенерированной HTML-разметке. Метод RegisterClientScriptBlock() предназначен для функций, которые вызываются в ответ на события JavaScript. Эти блоки <script> можно размещать в любом месте HTML-документа. Их помещение в начало веб-формы - всего лишь соглашение, облегчающее их поиск.

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

При использовании методов RegisterClientScriptBlock() и RegisterStartupScript() необходимо указывать также ключевое имя для блока сценария. Например, если функция открывает всплывающее окно, можно было бы использовать ключевое имя ShowPopUp. Фактическое ключевое имя не важно, пока оно остается уникальным. Главное обеспечить, чтобы ASP.NET не добавила одну и ту же функцию сценария более одного раза.

Это соображение наиболее важно при работе с серверными элементами управления, которые генерируют JavaScript-код. Например, рассмотрим элементы управления проверкой достоверности ASP.NET. Каждому такому элементу для работы требуются определенные функции проверки достоверности, но нет смысла, чтобы каждый элемент добавлял один и тот же блок <script>. Однако, поскольку каждый элемент управления использует то же самое ключевое имя при вызове метода RegisterClientScriptBlock(), среда ASP.NET понимает, что эти определения являются дубликатами, и визуализирует только одну копию.

Например, следующий код регистрирует JavaScript-функцию confirmSubmit(). Эта функция выводит на экран поле подтверждения и, в зависимости от того, на какой кнопке совершен щелчок - OK или Cancel (Отмена), выполняет обратную отправку страницы или ничего не делает. Затем эта функция присоединяется к форме через атрибут onsubmit:

protected void Page_Load(object sender, EventArgs e)
{
        string script = @"<script>
            function ConfirmSubmit() {
                var msg = 'Вы действительно хотите отправить данные?'; 
                return confirm(msg);
            }
        </script>";
        
        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), 
            "Confirm", script); 
        form1.Attributes.Add("onsubmit", "return ConfirmSubmit();");
}

Чтобы упростить определение функции JavaScript в нескольких строках, в начале должен быть помещен символ @, в результате все символы будут обрабатываться как строковые литералы, что позволяет использовать несколько строк.

Результат показан на рисунке ниже:

Использование подтверждающего сообщения JavaScript

В этом примере применение метода RegisterCllentScriptBlock() не дает никаких реальных преимуществ. Однако методы ClientScriptManager становятся важны при разработке специального элемента управления, который использует JavaScript. Позже будет рассмотрен элемент управления, в котором с помощью метода RegisterStartupScript() отображается всплывающее окно.

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