Спонсор: Microsoft
Опубликован: 21.03.2013 | Доступ: свободный | Студентов: 6312 / 126 | Длительность: 06:49:00
Лабораторная работа 2:

Обработка данных

HTML + JS: Практическое занятие №2

Стилизация приложения

Задание: продолжая проект, полученный в результате выполнения первого практического задания, измените внешний вид приложения в соответствии со спецификой выбранной темы (источников данных) и рекомендациями ниже.

  • Не забудьте убедиться, что ваше приложение корректно выглядит при разных размерах экрана и в разных состояниях (snap, fill, full).
  • В качестве подтверждения выполнения лабораторной работы от вас потребуется предоставить скриншот стилизованного приложения (главный экран).

Темная или светлая

Первое практическое задание мы закончили на приложении, которое выглядит примерно следующим образом:

Это стандартная темная тема приложения с темным фоном и светлым шрифтом. В своем приложении вы можете как целиком переопределить цветовую схему используя CSS, так и отталкиваться от базовой темы, изменяя лишь отдельные детали.

Windows Library for JavaScript, подключаемая по умолчанию во всех шаблонах, предоставляет вам две готовые темы: светлую и темную, описанные соответственно в файлах ui-light.css и ui-dark.css.

В заголовке каждой станицы вы легко найдете соответствующие ссылки:

<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />

Чтобы заменить темную тему на светлую, замените везде ui-dark.css на ui-light.css:

Связывание данных

Для начала давайте попробуем разобраться, как данные попадают на страницы. Для этого используется связывание данных (Data-Binding). В WinJS для этого есть специальные шаблоны, которые описываются через data-атрибуты как WinJS.Binding.Template.

Откройте страницу groupedItems.html и найдите описание шаблона с классом "itemtemplate":

<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
        <div class="item">
            <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
            <div class="item-overlay">
                <h4 class="item-title" data-win-bind="textContent: title"></h4>
                <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
            </div>
        </div>
    </div>

Этот шаблон используется для отображения плиток на главной странице приложения. Через data-win-bind атрибуты в нем прописано, как данные должны проецироваться на отображение, точнее в какие атрибуты html-элементов они должны быть прописаны при выполнении приложения.

Названия полей данных (в примере выше, это, например, backgroundImage, title, subtitle) соответствуют описанию данных в коде на JavaScript.

Откройте файл groupedItems.js и найдите функцию _initializeLayout—в ней как раз указывается, какой источник данных нужно использовать при наполнении нашего listView и далее в шаблонах:

listView.itemDataSource = Data.items.dataSource;

В свою очередь Data – это глобально доступный объект, определенный в файле data.js, который определяли в первом практическом упражнении. Давайте вернемся к этому файлу, чтобы доопределить недостающие нам данные (например, ссылку на изображение).

Перейдите к функции getItemsFromRSSFeed, занимающейся разбором отдельных постов. Добавьте перед строчкой "var postItem = {" следующий код:

var tempElement = document.createElement("div");
tempElement.innerHTML = postContent;
var image = tempElement.querySelector("img");
var imglink = (image != null) ? "url('" + image.src + "')" : "";

Здесь мы создаем временный элемент с контентом поста и извлекаем (querySelector) из него первую же картинку, чтобы использовать ее в качестве фоновой на плитках к постам. В случае, если картинки нет, используем пустую строку, которую передадим в соответствующее правило CSS.

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

Добавьте в объект-описание поста (postItem) следующие строчки в конце, не забыв добавить запятую строчкой выше:

backgroundImage: imglink

Должно получиться примерно вот так:

var postItem = {
    ...
    link: post.querySelector("link").textContent,
    backgroundImage: imglink
};

Если вы сейчас запустите проект на отладку, вы увидите, что визуально ничего не поменялось.

Вернитесь в файл pages\groupedItems\groupedItems.html к нашему шаблону itemtemplate. Двумя строчками ниже вы увидите описание картинки:

<img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />

Если вы знакомы с основами HTML, вы наверняка уже догадались, почему в отображении ничего не поменялось: формат, который мы использовали выше, рассчитан на использование в CSS, а не явную вставку ссылки на изображение через img.

Замечание: одна из причин, почему в данном случае CSS является предпочтительнее, заключается в более гибких настройках отображения и, в частности, наличие возможности манипуляций с изображением, в том числе с сохранением пропорций.

Удалите эту строчку для вставки изображения.

Поднимитесь на уровень выше и добавьте в строчке:

<div class="item">

связывание данных для фонового изображение через соответствующее свойство в CSS:

<div class="item" data-win-bind="style.backgroundImage: backgroundImage">

Попробуйте запустить приложение на отладку:

Изменение плиток

Стало лучше, но по-прежнему не очень. Например, что вполне ожидаемо, в некоторых постах нет изображений — и вместо них мы получили отсутствие картинок или пустые заглушки. Давайте для таких постов поставим цветной фон. Для этого откройте в той же папке файл groupedItems.css.

Найдите следующую строчку, описывающую отображение отдельного элемента:

.groupeditemspage .groupeditemslist .item {
 -ms-grid-columns: 1fr;
 -ms-grid-rows: 1fr 90px;
 display: -ms-grid;
 height: 250px;
 width: 250px;
    }

Добавьте в конце этого CSS-правила описание фона:

 background-color: rgb(0, 204, 255);

Дополнительно можно сделать еще несколько манипуляций над фоновым изображением, например, центрировать его, отменить повторения и задать режим масштабирования:

 background-position: 50% 50%;
 background-repeat: no-repeat;
 background-size: cover;

Запускаем приложение:

Теперь давайте перейдем к исправлению отображения текстового содержимого.

Обновление текстовых стилей

Вернитесь к файлу groupedItems.html и описанию шаблона элемента (itemtemplate). В конце элемента вы найдете описание подзаголовка (item-subtitle). В нашем случае никакого подзаголовка нет, зато есть дата, поэтому замените эту строчку:

<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>

на следующие:

 <h6 class="item-date win-type-ellipsis">
     <span data-win-bind="textContent: day"></span> 
     <span data-win-bind="textContent: month"></span>
 </h6>

Здесь мы также используем связывание с доступными нам полями в описании каждого поста: day и month. Аналогично вы можете определить любые другие необходимые поля и использовать их в отображении объектов.

Если вы попробуете сейчас запустить, вы увидите, что дата куда-то съехала и залезла на заголовок.

Чтобы это поправить, перейдите назад к CSS-файлу. Найдите описание подзаголовка:

.groupeditemspage .groupeditemslist .item .item-overlay .item-subtitle {
 -ms-grid-row: 2;
 width: 220px;
}

Замените его на соответствующее описание отображение даты:

.groupeditemspage .groupeditemslist .item .item-overlay .item-date {
    -ms-grid-row: 2;
    width: 220px;
    text-transform: uppercase;
    text-align:right;
}

В данном случае мы также добавили выравнивание по правому краю и отображение текста в верхнем регистре.

Замечание: обратите внимание, что для позиционирования элементов использует модуль CSS 3 Grid Layout. Мы не будем останавливаться на деталях его использования, но если вы планируете создавать приложения для Windows Store на HTML/JS, рекомендуем хорошо изучить его возможности.

Теперь давайте попробуем сделать заголовок записи побольше и изменить его положение на плитке.

Для начала его необходимо вынести на уровень выше. Перейдите к файлу groupedItems.html и внутри шаблона элемента вынесите описание заголовка на уровень выше, добавив обертку. Должно получиться примерно так:

<div class="item" data-win-bind="style.backgroundImage: backgroundImage">   
    <!-- Заголовок -->
    <div class="item-title-container">
        <h4 class="item-title" data-win-bind="textContent: title"></h4>
    </div>
    <div class="item-overlay">
        <h6 class="item-date win-type-ellipsis">
            <span data-win-bind="textContent: day"></span>
            <span data-win-bind="textContent: month"></span>
        </h6>
    </div>
</div>

Вернитесь к CSS-файлу, в нем необходимо отобразить изменение иерархии и прописать обновленные стили.

После правила .groupeditemspage .groupeditemslist .item { … } добавьте новое:

.groupeditemspage .groupeditemslist .item .item-title-container {
    -ms-grid-row: 1;    
    margin: 10px;
    padding: 8px;
    opacity: 0.85;
}

Сразу после него перенесите находящееся ниже описание заголовка (.item-overlay надо заменить на .item-title-container):

.groupeditemspage .groupeditemslist .item .item-title-container .item-title {
      overflow: hidden;
      display: inline; 
      font-size: 1.6em; 
      line-height: 1.5em;
      font-family: 'Segoe UI Light';
      background: rgb(145, 0, 145); 
      box-shadow: rgb(145, 0, 145) 0 0 0 8px;
  color: white;
     }

Попробуйте запустить проект:


Чтобы убрать подложку, оставшуюся от изначального макета, перейдите ниже по коду в CSS-файле к строчкам (оно находится внутри media query – описания):

.groupeditemspage .groupeditemslist .item .item-overlay {
  background: rgba(0,0,0,0.65);
}

Замените указанный цвет на transparent.

Попробуйте самостоятельно увеличить размер текста у даты и поменять шрифт на "Segoe UI Semibold" и название приложения на другое. Самостоятельно попробуйте поменять внешний вид заголовков, чтобы они приняли примерно такой вид (это может быть удобнее делать в Expression Blend):


Подберите наиболее подходящий индивидуальный стиль для вашего приложения.

Внутренние страницы и режимы отображения

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

Сделайте это самостоятельно на свое усмотрение. Например, для блогов, посвященных Windows это могло бы выглядеть так:



Переведите приложение в Snapped-режим (если вы работаете в Expression Blend, на вкладке Device можно менять режим отображения). Здесь также нужно поправить стили с учетом выбранной стилистики.

Причем корректность отображения необходимо проверить для всех внутренних экранов.

Стили для Snap-режима корректируются с помощью Media Queries:

@media screen and (-ms-view-state: snapped) {
...
}

Ограничение вывода данных

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

В конечном счете, в нашем случае вместо того, чтобы показывать быстро и целиком всю картину последних событий, для чего было бы достаточным выводить 6-8 последних записей, мы сразу обрушиваем на пользователя весь поток новостей. Большой набор данных также не дает возможности быстро переключаться на соседние группы — приходится слишком долго прокручивать (забегая сильно вперед, скажу, что частично эту проблему сглаживает Semantic Zoom).

Таким образом, наша первоочередная задача — ограничить поток данных на первом экране.

Для этого откройте файл pages\groupedItems\groupedItems.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), сгруппированных по принадлежности к тем или иным RSS-потокам.

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

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

Далее в этой же функции замените встречающиеся ниже ссылки на Data и Data.items на переменную groupedItems:

if (viewState === appViewState.snapped) {
    listView.itemDataSource = groupedItems.groups.dataSource;
    listView.groupDataSource = null;
    listView.layout = new ui.ListLayout();
} else {
    listView.itemDataSource = groupedItems.dataSource;
    listView.groupDataSource = groupedItems.groups.dataSource;
    listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
}

Аналогичную операцию проделайте внутри функции _itemInvoked:

_itemInvoked: function (args) {
    if (appView.value === appViewState.snapped) {
        // If the page is snapped, the user invoked a group.
        var group = groupedItems.groups.getAt(args.detail.itemIndex);
        this.navigateToGroup(group.key);
    } else {
        // If the page is not snapped, the user invoked an item.
        var item = groupedItems.getAt(args.detail.itemIndex);
        nav.navigate("/pages/itemDetail/itemDetail.html", { item: Data.getItemReference(item) });
    }
} 

Запустите проект:


Отлично! Теперь мы сразу видим все самое свежее.

Дополнительно

Если вы хотите дополнительно разнообразить внешний вид вашего приложения, попробуйте добавить поддержку плиток разного размера.

Как это сделать, описано в этом руководстве: http://msdn.microsoft.com/ru-ru/library/windows/apps/jj657974.aspx.

Андрей Милютин
Андрей Милютин

Будьте добры сообщите какой срок проверки заданий и каким способом я буду оповещен!

Данила Слупский
Данила Слупский

К сожалению, я не могу выполнить данную практическую работу в VS 2013 на WIndows 8.1. Код описанных файлов отличается от кода в моем проекте. Как мне быть?