Отложенная загрузка части страницы

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

Решено было поставить просто OutputCache, но он был убран, скорее всего ввиду того, что данные обновлялись несколько чаще, чем происходило обновление кэша, потому, когда я преступил к работе – его уже не было. Кэшировались только запросы, хотя тоже не особо помогло.

Собственно – была поставлена задача сделать отложенную загрузку части страницы, и как – не важно, лишь бы ajax, догружающий на window.onload страницы ее часть, и решение было применимо при условии жесткой связи контролов страницы (которые следовало догружать) со страницами, настройками текущего пользователя и даже с контролами на MasterPage.
Итак, задание есть – реализовать нужно сегодня.

Первым делом решил погуглить, наткнулся на статью Гайдара Магданурова - Отложенная загрузка фрагментов страницы, но вариант подгружать контролы «вручную», не подходит для меня, т.к. необходимо сохранить его работоспособность, т.е. постбеки и связь с мастер-страницей, хотя в голове и мелькал изврат по созданию экземпляра не просто Page, а MyPage J, но это реально изврат.


Обходить через делегаты вызовы методов со страниц ну очень не хотелось, мне так посоветовали на http://inln.ru/Blog/post/Lazy-Load-Control.aspx

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

Каким образом можно поставить на обновление части страницы:


<cms:DeferredUpdatePanelUpdatePriority="0" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <%=DateTime.Now.Millisecond %>
    </ContentTemplate>
</cms:DeferredUpdatePanel>

<cms:DeferredUpdatePanelUpdatePriority="2" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <%= DateTime.Now.Millisecond %>
    </ContentTemplate>
</cms:DeferredUpdatePanel>


Сначала загрузится первая панель, потом вторая, за это отвечает свойство UpdatePriority, чем оно выше – тем позденее панель обновится.

Но что делать, если нужно вывести именно данные? Тут на помощь приходит событие OnDeferredUpdated – оно вызывается, когда панель необходимо обновить, точнее – когда он сам хочет обновиться, а именно – при первой загрузке страницы.

Для теста на странице разместим даталист, выводящий нам все числа, от 1 до 200, но выводится они будут только после загрузки страницы, т.е. подгрузятся аяксом:



<cms:DeferredUpdatePanelID="defPanel" UpdatePriority="2" runat="server" UpdateMode="Conditional" OnDeferredUpdated="defPanel_OnDeferredUpdated" OnPreUpdatingJS="onPreUpdating" OnUpdatingJS="onUpdating" OnUpdatedJS="onUpdated">
    <ContentTemplate>
        <asp:DataList runat="server" RepeatColumns="20" ID="listNothing" Width="100%" BorderWidth="1" BorderStyle="Dotted" CellPadding="0" CellSpacing="0">
            <ItemStyle Width="5%" BorderStyle="Groove" />
                <ItemTemplate>
                    <%# Container.DataItem %>
                </ItemTemplate>
        </asp:DataList>
    </ContentTemplate>
</cms:DeferredUpdatePanel>



Заметьте, что есть обработчик события обновления OnDeferredUpdated:

protectedvoid defPanel_OnDeferredUpdated(object sender, DeferredUpdatePanelEventArgs e)
{
     List<int> list = new List<int>();

     for (int i = 0; i < 100; i++)
           list.Add(i);

      listNothing.DataSource = list;
      listNothing.DataBind();
}

Именно в нем то мы и биндим все необходимые данные. Возможно кто-то скажет – «Да это и так просто можно сделать на OnLoad+ пара скриптов», что же, он будет прав, но если таких панелей 3 на странице – то код уже становится некрасивым и на обладку у новичка уйдет от получаса до часа, так что – лучше просто иметь подобный контрол.
Какие плюсы у этого контрола:
  1. Не нужно все делать самому

  2. Есть обработчик обновления – отличается от OnLoadименно тем, что для каждого UpdatePanelон свой, т.е. есть проверка на ClientID

  3. Есть возможность задать порядок обновления

  4. Вызываются JavaScriptфункции для подготовки части страницы перед обновлением, во время обновления и после обновления, при чем в эти функции передаются сами апдейтпанели (чисто для удобства, сокращает 2 строки кода для каждого из событий)
  1. Названия функций прописывать в свойствах: OnPreUpdatingJS, OnUpdatingJS, и OnUpdatedJS.

  2. Сохраняется событийная модель!

  3. Сохраняется связь контрола со страницей, с другими контролами или мастер-страницами

  4. Переделывать практический ничего не нужно, максимум – переместить код из обного места в другое и все
Надеюсь, заметка будет кому-нибудь полезна, по крайней мере – нам подобное решение немного приукрасила жизнь J
Демонстрационный пример можно увидеть на тестовом сайте и скачать оттуда же :)
UPD:

8 комментариев:

  1. Интересное решение! У нас возникла та же проблема... попробуем прикрутить в своем проекте

    ОтветитьУдалить
  2. Да... а про полный page-life-cycle слышали? Это же сколько ресурсов надо потратить...

    ОтветитьУдалить
  3. конечно в курсе, но в asp.net при всех постбеках есть восстановление состояния и проходит весь жизненный цикл страницы
    ресурсов - сожрет не много, если не биндить уже сбинденные данные

    ОтветитьУдалить
  4. можете выложить тестовый пример? ссылка не доступна

    ОтветитьУдалить
  5. Ссылка на сам контрол: https://skydrive.live.com/redir.aspx?cid=d8340e05f5504a6a&resid=D8340E05F5504A6A!367

    ОтветитьУдалить
  6. Довольно интересная статья.
    могли бы вы еще раз выложить код контрола (старые ссылки не работают)

    ОтветитьУдалить

Можете оставить свой комментарий