- PVSM.RU - https://www.pvsm.ru -
Эта статья продолжает серию материалов (первая часть [1], вторая часть [2]), посвященных азам разработки WinRT-приложений на HTML/JS для Windows 8. В этой части мы постараемся улучшить надежность получения и качество отображения данных, а также немного поговорим о контрактах.
Напомню, что в предыдущей части мы остановились на том, что научились получать данные из внешних RSS-потоков и изменили стили отображения данных для различных состояний приложения, включая snapped-режим.
Если вы попробуете еще раз проследить, как получаются и отображаются данные, вы легко заметите, что мы их никак не ограничиваем. Приложение получает на входе потоки данных из RSS, преобразовывает их во внутреннюю структуру, используемую для связывания данных, и далее проецирует их в коллекцию на экране. Фактически, мы сразу видим все-все данные прямо с первого экрана.
Отмечу, что особенностью данного решения является то, что фактически мы всегда имеем на руках всю доступную информацию. В сложном проекте с бо́льшим количеством данных, возможно, серверной логикой и наличием специальных API, скорее всего, имело бы смысл запрашивать данные по мере необходимости, подгружая сначала только заголовки. Но это уже другая история. :)
В конечном счете, в нашем случае вместо того, чтобы показывать быстро и целиком всю картину последних событий, для чего было бы достаточным выводить 6-8 последних записей, мы сразу обрушиваем на пользователя весь поток новостей. Большой набор данных также не дает возможности быстро переключаться на соседние группы — приходится слишком долго прокручивать (забегая сильно вперед, скажу, что частично эту проблему сглаживает Semantic Zoom).
Таким образом, наша первоочередная задача — ограничить поток данных на первом экране.
Для этого откройте файл pagesgroupedItemsgroupedItems.js.
В начале функции в области переменных создайте еще две:
var groupedItems;
var tilesLimit = 6;
В первой мы будем хранить отфильтрованную коллекцию. Вторая указывает, сколько максимум плиток можно выводить (в идеале, подобные параметры нужно выносить в отдельные файлы настроек или аналогичные структуры данных, описывающих параметры проекта).
Далее найдите следующую строчку:
_initializeLayout: function (listView, viewState) {
Эта функция вызывается при инициализации страницы и прописывает, какие данные необходимо вывести на экран в зависимости от используемого режима отображения. В качестве источника данных используется глобальный объект Data, описанный в файле data.js.
Добавьте в начале функции следующие строчки:
groupedItems = Data.items.createFiltered(function (item) {
return item.index < tilesLimit;
}).createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
Напомню, что в свойстве items объекта Data прописана коллекция данных (WinJS.Binding.List [3]), сгруппированных по принадлежности к тем или иным RSS-потокам.
Чтобы отфильтровать данные, мы берем эту коллекцию и оставляем только элементы, индекс которых меньше нашего ограничения, после чего заново группируем коллекцию в соответствии с группами.
Замечание: в данном случае мы явно ограничиваем количество элементов шестью (tilesLimit), однако, в общем случае это неправильный подход. На большом мониторе количество плиток может оказаться меньше желаемого, к тому же могут появляться одиноко висящие плитки в конце списка. Самое правильное: динамически рассчитывать лимит в зависимости от доступного пространства.
Далее в этой же функции замените встречающиеся ниже ссылки на Data и Data.items на переменную groupedItems:
if (viewState === appViewState.snapped) {
listView.itemDataSource = <mark>groupedItems</mark>.groups.dataSource;
listView.groupDataSource = null;
listView.layout = new ui.ListLayout();
} else {
listView.itemDataSource = <mark>groupedItems</mark>.dataSource;
listView.groupDataSource = <mark>groupedItems</mark>.groups.dataSource;
listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
}
Замечание: будьте внимательны с другими вхождениями Data на данной странице. В данном случае мы их не изменяем, так как они используются при переходе ко внутренним страницам, а выше мы всего лишь сделали фильтр по данным. Но в общем случае, если вы меняете источник данных, его, возможно, надо менять везде, либо, что правильнее, вынести отдельно слой работы с данными.
Запустите проект:
Отлично! Теперь мы сразу видим все самое свежее.
Следующий важный пункт: убедиться, что приложение корректно себя ведет при отсутствии интернет-соединения. Если вы попробуете запустить текущую версию приложения с отключенной сетью, например, включив Airplane-режим, оно должно вызвать ошибку из-за невозможности скачать нужные поток. (Возможно, они успели закешироваться, поэтому для надежности эксперимента можно подключить какие-нибудь другие потоки.)
Кроме того, если поток окажется недоступным, также возникнет проблема.
Чтобы проверить наличие интернета, откройте файл jsdata.js и добавьте в него следующую функцию:
function isInternetConnection() {
var connectionProfile = Windows.Networking.Connectivity.NetworkInformation.getInternetConnectionProfile();
return (connectionProfile != null);
}
Здесь мы обращаемся к WinRT, чтобы узнать текущее состояние сети. Это уже хорошая информация, хотя она и не дает 100% гарантии, что доступ к интернету и нужному потоку действительно есть.
Теперь давайте добавим функцию, которая будет выводить сообщение об ошибке при наличии проблем:
function showConnectionError(msg, donotexit) {
msg = (msg != undefined) ? msg : "";
var messageDialog = new Windows.UI.Popups.MessageDialog(msg, "Can not connect");
messageDialog.commands.append(new Windows.UI.Popups.UICommand("Ok", null, 1));
messageDialog.showAsync().done(function (command) {
if (!donotexit && command.id == 1) {
MSApp.terminateApp({
number: 0,
stack: "",
description: "No internet connection"
});
}
});
}
Данная функция, используя WinRT API, выводит заданное сообщение об ошибке и, по умолчанию, если не выставлен флаг donotexit (или он равен false), завершает приложение.
Замечание: в реальном приложении логика поведения может отличаться. Например, приложение может намеренно кешировать данные или предлагать пользователю попробовать еще раз скачать потоки с сервера, если была временная потеря соединения.
Следующий шаг: запустить проверку наличия соединения, для этого замените строчку
list = getBlogPosts(list);
на следующие:
function tryUpdateData() {
if (isInternetConnection()) {
list = getBlogPosts(list);
} else {
showConnectionError("Please check your internet connection. ");
}
};
tryUpdateData();
Фактически, мы обернули обращение данных в проверку наличия интернет-соединения. Если его нет, приложение выдает сообщение об ошибке и закрывается:
Теперь давайте вернемся к функции getBlogPosts. Здесь тоже есть вероятность получения ошибки, например, если какой-то RSS-поток перестал работать, в этом случае наша обертка над XHR вызовет исключение, которое нужно перехватить.
Попробуйте заменить одну из ссылок на RSS на неправильную и запустить приложение (здесь мы предполагаем, что ответ сервера будет соответствующим неправильной ссылке).
Чтобы обработать исключительную ситуацию, в описании Promise нужно добавить функцию, вызываемую при возникновении ошибки. Сразу после внутренней анонимной функции function (response) {…} добавьте через запятую:
function (error) {
showConnectionError("Can't get rss updates for " + feed.title + ". Used source: " + feed.url, true);
}
В данном случае мы выводим сообщение об ошибке, но не закрываем приложение: возможно, другие потоки обработаются нормально.
Попробуйте сделать одну из ссылок неправильной и запустить приложение:
Ура! Теперь мы можем радовать пользователя злостными сообщениями. Тут я хочу повторить еще раз важную мысль: подобное информирование пользователя — это всего лишь полумера. В идеале нужно кешировать данные и информировать пользователя о наличии проблем с соединением менее разрушительным способом. См. например, как работает приложение Bing News в Windows 8.
Напоследок в этой статье мы научимся делиться информацией из приложения наружу, используя контракты общего доступа (sharing) и печати.
Откройте файл pagesitemDetailitemDetail.js. Добавьте в начале переменную для хранения обработчика событий:
var sharingHandler;
Добавьте также в начале или ближе к концу (вне WinJS.UI.Pages.define()) следующие функции:
1. setupSharing — регистрация на передачу данных
function setupSharing(item) {
var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
sharingHandler = onSharingDataRequested.bind(item);
dataTransferManager.addEventListener("datarequested", sharingHandler);
}
Здесь мы обращаемся через WinRT к менеджеру передачи данных и вешаем обработчик события запроса данных.
2. onSharingDataRequested — событие для обработки запроса
function onSharingDataRequested(e) {
var request = e.request;
var item = this;
var text = item.title;
if (item.link) {
text += "nr" + item.link;
request.data.setUri(new Windows.Foundation.Uri(item.link));
}
request.data.properties.title = text;
request.data.properties.description = "Some awesome news on Windows!";
request.data.setText(text);
}
Здесь мы описываем, какие данные мы передаем приложению-получателю: ссылку, текст и т.п. (подробности передачи данных можно посмотреть здесь: общий доступ к содержимому и его получение [4]).
Внутри функции ready в самом конце добавьте вызов регистрации на поддержку контракта:
setupSharing(item);
Запустите приложение, перейдите к посту и через панель чудо-кнопок попробуйте передать новость в другое приложение:
Осталось добавить еще одну очень важную деталь. Если вы попробуете вернуться на уровень выше (уйти с текущей страницы) и используете контракт общего доступа (Sharing), вы увидите, что он по-прежнему «реагирует» и предлагает пользователю несколько вариантов на выбор. Это не то, что мы бы ожидали в качестве правильного поведения приложения.
Чтобы такого не происходило, необходимо сбросить обработчики событий в менеджере передачи данных. Это можно делать на входе в каждую страницу, либо наоборот на выходе. Добавьте следующую функцию:
function clearContracts() {
var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
dataTransferManager.removeEventListener("datarequested", sharingHandler);
}
Здесь мы просто удаляем соответствующий обработчик событий. Для вызова функции после описания свойства ready через запятую добавьте функцию для unload:
unload: function () {
clearContracts();
}
Готово, теперь передача данных работает только там, где она и должна работать.
Теперь давайте добавим поддержку печати. Прежде всего, надо отметить, что по умолчанию ваше приложение не умеет печатать. Для передачи документов на печать используется системный контракт печати (Print [5]), доступный пользователям через чудо-кнопку «Устройства»:
Чтобы добавить возможность печати в том же файле pagesitemDetailitemDetail.js (это наш контекст печати), необходимо подписаться на соответствующий менеджер аналогично тому, как мы это делали для общего доступа. Добавьте переменную для хранения обработчика:
var printTaskHandler;
функцию для подписи на контракт печати:
function setupPrinting(item) {
var printManager = Windows.Graphics.Printing.PrintManager.getForCurrentView();
printTaskHandler = onPrintTaskRequested.bind(item)
printManager.addEventListener("printtaskrequested", printTaskHandler);
}
и обработчики событий:
function onPrintTaskRequested(printEvent) {
var printTask = printEvent.request.createPrintTask(this.group.title, function (args) {
args.setSource(MSApp.getHtmlPrintDocumentSource(document));
});
}
Здесь вы также можете настраивать параметры печати и подписаться на дополнительные события,
например, обработать событие окончания печати.
Внутри функции ready в самом конце добавьте вызов регистрации на поддержку контракта:
setupPrinting(item);
и не забудьте в clearContracts удалить обработчик запроса печати:
var printManager = Windows.Graphics.Printing.PrintManager.getForCurrentView();
printManager.removeEventListener("printtaskrequested", printTaskHandler);
Попробуйте запустить приложение и отправить статью на печать:
Если вы достаточно наблюдательны, вы должны сразу увидеть возникшую проблему: на печать отправлена только одна страница, причем в том виде, как выглядит страница на экране (с горизонтальной прокруткой).
Аналогично тому, как бы вы решали эту проблему для веба, здесь нам на помощь приходят media-запросы и CSS-правила для печати. (Вы также можете передать на печать специально сгенерированную страницу, фрагмент, iframe и т.п.)
Добавьте в проект новый файл pagesitemDetailprint.css, либо просто запишите приведенный ниже код в css-файл, привязанный к нужной странице (itemDetail.css). Добавьте в него следующие строчки:
@media print {
/* изменяем отступы страницы*/
.itemdetailpage.fragment {
-ms-grid-rows: 40px 1fr;
}
/* изменяем расположение заголовка страницы*/
header[role="banner"] {
-ms-grid-columns: 40px 1fr !important;
}
/* прячем кнопку "назад" */
.win-backbutton {
display:none;
}
/* изменяем отступы заголовка и шрифт страницы*/
header[role="banner"] h1.titlearea {
margin: 0px !important;
font-size: 1em;
text-transform: uppercase;
}
/* изменяем высоту контента и перехлесты для вертикальной прокрутки */
.itemdetailpage.fragment, .itemdetailpage .content, .itemdetailpage .content article {
height:auto !important;
overflow: visible !important;
}
/* изменяем отступы контента*/
.content {
padding-left: 40px;
}
/* убираем многоколоночную верстку */
.itemdetailpage .content article {
column-width: auto;
width: auto;
margin-left: 0px;
margin-top: 40px;
}
}
Запускаем приложение и пробуем отправить на печать:
На этом мы закончим текущую статью.
Готовый проект на текущей стадии можно скачать тут: http://sdrv.ms/V1InN9 [6].
В следующей части мы продолжим разбираться с отображением и посмотрим, как использовать
более сложные шаблоны и добавить поддержку контекстного масштабирования.
Автор: kichik
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/24882
Ссылки в тексте:
[1] первая часть: http://habrahabr.ru/company/microsoft/blog/163443/
[2] вторая часть: http://habrahabr.ru/company/microsoft/blog/163813/
[3] WinJS.Binding.List: http://msdn.microsoft.com/en-us/library/windows/apps/hh700774.aspx
[4] общий доступ к содержимому и его получение: http://msdn.microsoft.com/ru-ru/library/windows/apps/hh758314.aspx
[5] Print: http://msdn.microsoft.com/ru-ru/library/windows/apps/hh465225.aspx
[6] http://sdrv.ms/V1InN9: http://sdrv.ms/V1InN9
[7] Источник: http://habrahabr.ru/post/165909/
Нажмите здесь для печати.