Отложенная загрузка фрагментов страницы
Продолжая опубликованный ранее пост про получение HTML разметки контрола, рассмотрим один из возможных вариантов применения разметки. Например, используем ее для того, чтобы загрузить фрагмент страницы, который может загружаться длительное время (зависимость от медленного источника данных, необходимость длительных операций по генерации или подготовке данных и т.п.), после загрузки основного фрагмента страницы. Для этого нужно создать механизм отложенной загрузки гаджетов, реализованных в виде элементов управления.
Рассмотрим пример такой страницы:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DemoWebApp._Default" %>
<%@ Register Src="~/Pages/DataControl.ascx" TagPrefix="My" TagName="DataControl" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<table>
<tr>
<td style="width: 34%;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras mattis, erat ac mollis
ultrices, tortor risus hendrerit est, at laoreet ante enim sit amet velit.
</td>
<td style="width: 33%;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras mattis, erat ac mollis
ultrices, tortor risus hendrerit est, at laoreet ante enim sit amet velit.
</td>
<td style="width: 33%;">
<My:DataControl runat="server" />
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Элемент управления DataControl.ascx загружает данные из удаленного веб-сервиса, который имеет тенденцию отдавать данные очень медленно. Соответственно в случае задержки на стороне удаленного сервиса страница будет загружаться очень долго и пользователь (или поисковый робот) может не дождаться загрузки страницы. Поэтому мы доработаем эту страницу и сделаем так, чтобы содержимое элемента управления загружалось уже после полной загрузки страницы.
Для этого мы воспользуемся Ajax запросом, обращающемся к следующей веб-службе:
namespace DemoWebApp
{
[WebService(Namespace = "https://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class PageService : System.Web.Services.WebService
{
[WebMethod]
public string GetDataGadget()
{
Thread.Sleep(5000); // эмулируем тормоза
Page pg = new Page();
UserControl uc = (UserControl)pg.LoadControl("~/Pages/DataControl.ascx");
pg.Controls.Add(uc);
StringWriter sw = new StringWriter();
HttpContext.Current.Server.Execute(pg, sw, false);
return sw.ToString();
}
}
}
Особенно отмечу, что нужно пометить службу атрибутом ScriptService, чтобы для нее был сгенерирован proxy-класс на JavaScript, который мы будем использовать в Ajax коде.
Теперь код страницы можно переделать для того, чтобы поддерживать отложенную загрузку:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DemoWebApp._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="~/PageService.asmx" />
</Services>
</asp:ScriptManager>
<div>
<table>
<tr>
<td style="width: 34%;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras mattis, erat ac mollis
ultrices, tortor risus hendrerit est, at laoreet ante enim sit amet velit.
</td>
<td style="width: 33%;">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras mattis, erat ac mollis
ultrices, tortor risus hendrerit est, at laoreet ante enim sit amet velit.
</td>
<td style="width: 33%;">
<div id="gadget">
Loading...</div>
</td>
</tr>
</table>
</div>
</form><script type="text/javascript">
Sys.Application.add_init(page_Init);
function page_Init() {
DemoWebApp.PageService.GetDataGadget(gadget_OnSuccess, gadget_OnError);
}function gadget_OnSuccess(result) {
// Sys.UI.DomElement.removeCssClass($get('RSSBlock'), 'loading');
$get('gadget').innerHTML = result;
}function gadget_OnError() {
$get('gadget').innerHTML = 'Loading error.';
}
</script></body>
</html>
Вот и все. Теперь загрузка “медленного” блока будет происходить после загрузки страницы. И пользователь сначала увидит нечто вроде:
А по мере загрузки блока, пользователь увидит и полноценный блок:
Теперь можно навешать много разной красоты, чтобы пользователю было не скучно: например, в div, в который загружается код гаджета, поместить какую-нибудь анимированную картинку.
Описанный выше подход позволит развязать время загрузки основной страницы и зависящих от медленных источников данных фрагментов – таким образом пользователь может увидеть основной контент страницы в любом случае, даже если данные из удаленного источника получить не получится.
Следующим шагом можно настроить кеширование данных, которые возвращает веб-служба, если можно пожертвовать актуальностью данных, выводимых в блок, а для упрощения добавления таких блоков на страницу можно сделать простой элемент управления, которому в качестве параметров передавать адрес службы и имя метода для загрузки данных гаджета.
Comments
Anonymous
March 20, 2010
Было бы здорово, если бы в иерархии контролов ASP.NET был некий PostponedControl со свойством "Отложить загрузку". Выставил это PostponeLoad в true, и ASP.NET сама генерировала бы описанную в посте обвязку : )Anonymous
April 02, 2010
Доброго времени суток как раз возникла задача реализовать отложенную загрузку части страницы, но ваш метод не подходил (нужны были события и была связь со страницей и мастерпейджем), потому реализовал свой контрол, на основе апдейт панела, и делается немного проще (если быть точнее - мой метод немного проще юзается) подробно написал тут: http://inln.blogspot.com/2010/04/blog-post.html