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

Контекстное масштабирование и альтернативные шаблоны

< Онлайн-консультация 1 || Лабораторная работа 4: 12

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

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

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

ЗАМЕЧАНИЕ: напоминаем, что ваше приложение должно быть уникальным:

  • Использовать в качестве источников данных источники, отличные от тех, которые приводятся в инструкциям к практическим занятиям
  • Внешний вид приложения должен соответствовать выбранной вами тематике приложения (для всех страниц приложения).
  • Политика конфиденциальности (Privacy Policy) должна соответствовать вашему приложению и быть доступна внутри приложения.

Контекстное масштабирование

Как вы уже наверняка, знаете из лекций, контекстное масштабирование – это способ показать еще одно, -- более высокоуровневое, -- представление отображаемых данных. На практике контекстное масштабирование (Semantic Zoom) применяется, прежде всего, для предоставления возможности быстрого перемещения по длинным коллекциям данных.

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

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

<div class="groupeditemslist win-selectionstylefilled" aria-label="List of groups" 
data-win-control="WinJS.UI.ListView" data-win-options="{ selectionMode: 'none' }"></div>

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

groupedItems = Data.items.createFiltered(function (item) {
    return item.index < tilesLimit;

}).createGrouped(
    function groupKeySelector(item) { return item.group.key; },
    function groupDataSelector(item) { return item.group; }
);

В списке WinJS.Binding.List для этого доступен метод createGrouped, принимающий на входе функции, описывающие способ группировки на основании имеющихся данных.

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

Первым делом, на странице groupedItems.html добавьте шаблон для общего представления заголовков (в режиме масштабирования):

<div id="semanticZoomTemplate" data-win-control="WinJS.Binding.Template">
    <div class="semanticZoomItem">
        <h1 class="semanticZoomItem-Text" data-win-bind="textContent: title"></h1>
    </div>	
</div>

В данном случае содержание шаблона очень похоже на содержание описанного в этом же файле шаблона headerTemplate – нам также нужно извлечение названия заголовка (для начала).

Далее необходимо добавить еще один элемент ListView, с которым мы будем связывать данные в "сжатом" состоянии:

<!-- Сжатое представление -->
    <div class="zoomeditemslist win-selectionstylefilled" aria-label="Zoomed List of groups" 
         data-win-control="WinJS.UI.ListView" 
         data-win-options="{ selectionMode: 'none', tapBehavior: 'invoke', itemTemplate: select('#semanticZoomTemplate') }"></div>

Кстати, обратите внимание, что в данном случае используемый шаблон для представления данных мы прописали непосредственно при объявлении элемента управления. Это можно делать как декларативно, так и в коде.

Наконец, надо обернуть это все в один элемент управления Semantic View:

<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">    
    <!-- Детальное представление -->
    <div class="groupeditemslist win-selectionstylefilled" aria-label="List of groups" 
         data-win-control="WinJS.UI.ListView" 
         data-win-options="{ selectionMode: 'none' }"></div>

    <!-- Сжатое представление -->
    <div class="zoomeditemslist win-selectionstylefilled" aria-label="Zoomed List of groups" 
         data-win-control="WinJS.UI.ListView" 
         data-win-options="{ selectionMode: 'none', tapBehavior: 'invoke', itemTemplate: select('#semanticZoomTemplate') }"></div>

</div> 

Важно: порядок следования элементов (сначала детальное, потом сжатое) имеет значение!

Следующим шагом осталось прописать наши данные с используемыми шаблонами. Для этого перейдите в файл groupedItems.js и найдите функцию ready. После объявления переменной listView и ее инициализации добавьте еще одну переменную:

var zoomedListView = element.querySelector(".zoomeditemslist").winControl;

Далее замените вызов функции _initializeLayout на такой:

this._initializeLayout(listView, zoomedListView, appView.value);

Перейдите к функции _initializeLayout и поменяйте соответствующим образом ее сигнатуру:

_initializeLayout: function (listView, zoomedListView, viewState) { ... }

Внутри функции найдите блок выбора настройки данных в зависимости от режима представления if (…) {…} else {…} и внутри конструкции else {…} добавьте инициализацию zoomedListView группированными данными:

zoomedListView.itemDataSource = groupedItems.groups.dataSource;
zoomedListView.layout = new ui.GridLayout();

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



Контекстное масштабирование работает, но верстка "поехала". Чтобы все поправить, перейдите в файл groupedItems.css. Первым делом необходимо отменить поведение по умолчанию для контейнера semanticZoomDiv, для этого пропишите соответствующее правило, растягивающее его на все доступное пространство:

#semanticZoomDiv {
    height: 100%;
    width: 100%;
}

Теперь в обычном представлении все встало на свои места:


Далее, необходимо определить стили для сжатого представления:

.groupeditemspage .zoomeditemslist .win-horizontal.win-viewport .win-surface {
    margin: 128px 115px 60px 120px;
}

    .groupeditemspage .zoomeditemslist .semanticZoomItem {
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr 90px;
        display: -ms-grid;
        height: 250px;
        width: 250px;
        background-color: rgb(161, 38, 161);

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

     .groupeditemspage .zoomeditemslist .semanticZoomItem .semanticZoomItem-Text {
           font-size: 1.6em; 
           line-height: 1.2;
           font-family: 'Segoe UI Light';
           color: white;
           text-transform: uppercase;
           margin: 20px;
     }

Запустите проект и перейдите в режим контекстного масштабирования:


Попробуйте самостоятельно ввести на плитку дату (можно попробовать сделать связывание с полем ‘updated’) или картинку из последнего поста.

Теперь, если вы попробуете перейти в Snapped-режим, вы увидите, что все работает неправильно. И, в общем-то, если посмотреть в код, понятно, почему. Внутри функции updateLayout у нас есть еще один вызов функции инициализации отображения, но со старой сигнатурой:

this._initializeLayout(listView, viewState);

Исправьте это, добавив в начале объявление дополнительной переменной, как мы это делали в функции ready:

var zoomedListView = element.querySelector(".zoomeditemslist").winControl;

И поменяв соответствующим образом вызов функции initializeLayout:

this._initializeLayout(listView, zoomedListView, viewState);

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


Перейдите в функию _initializeLayout и добавьте перед if:

var semanticZoom = document.querySelector("#semanticZoomDiv").winControl;

Здесь мы получаем доступ к нашему элементу SemanticZoom, после чего внутри if (для snapped-режима) необходимо его отключить:

semanticZoom.enableButton = false;
semanticZoom.zoomedOut = false;
semanticZoom.locked = true;

А внутри else (для full и filled) наоборот разрешить:

semanticZoom.enableButton = true;
semanticZoom.zoomedOut = false;
semanticZoom.locked = false;

Готово!

Дополнительные шаблоны

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

Например, если мы добавим в список источников поток из Twitter, то он будет отображаться точно также, как и большие новости через RSS:

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

Главная страница

Первым делом надо научиться различать разные типы данных. Проще всего это сделать непосредственно при описании данных. Откройте файл с заданием источников данных и добавьте к каждому источнику поле ‘type’:

[{ "key": "ATwitter", "type": "twitter", "url": "https://api.twitter.com/1/statuses/user_timeline.rss?screen_name=windows", 
"title": "@windows", "rsstitle": "tbd", "updated": "tbd" }, { "key": "CBlogging", "type": "blog", "url": 
"http://blogs.windows.com/windows/b/bloggingwindows/rss.aspx", "title": "Blogging Windows", 
"rsstitle": "tbd", "updated": "tbd" }, { "key": "BExperience", "type": "blog", "url": 
"http://blogs.windows.com/windows/b/windowsexperience/rss.aspx", 
"title": "Windows Experience", "rsstitle": "tbd", "updated": "tbd" }, 
{ "key": "DExtreme", "type": "blog", "url": "http://blogs.windows.com/windows/b/extremewindows/rss.aspx", 
"title": "Extreme Windows", "rsstitle": "tbd", "updated": "tbd" }] 

Напомню, что в вашем случае это будут другие данные. Здесь мы также добавили новый тип источника – Twitter, для чего можно использовать Twitter API, подставив в запрос название твиттера (в нашем случае это @windows).

Теперь поле ‘type’ доступно для связывания в шаблонах для каждого элемента через свойство group.type. Наиболее оптимальный способ указать этот тип в шаблоне – прописать его в css-класс элемента. Это можно сделать двумя способами:

1. В шаблоне элемента (groupedItems.html) сделать связывание с классом, например так:

<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
        <div data-win-bind="className: group.type">
            <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>
        </div>
</div>

Здесь мы добавили вокруг элемента с классом "item" еще один контейнер, в котором прописали нужный нам класс, связав его со свойством group.type.

2. Из кода при инициализации listView вывести выбор шаблона в отдельную функцию, которая будет применять тот или иной класс и самостоятельно прописывать связывание.

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

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

Перейдите в groupedItems.css, найдите описание (они описаны отдельно, а не внутри media-query для snapped-режима):

.groupeditemspage .groupeditemslist .item {
    …
}

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

/* Twitter */
 .groupeditemspage .groupeditemslist .twitter .item {
}

.groupeditemspage .groupeditemslist .twitter .item .item-title-container {
}

    .groupeditemspage .groupeditemslist .twitter .item .item-title-container .item-title {
    }

.groupeditemspage .groupeditemslist .twitter .item .item-overlay {
}

    .groupeditemspage .groupeditemslist .twitter .item .item-overlay .item-date {
    }

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

.groupeditemspage .groupeditemslist .twitter .item {
           background-color: #012f41;
 }

     .groupeditemspage .groupeditemslist .twitter .item .item-title-container {
         background-image: none;
     }

         .groupeditemspage .groupeditemslist .twitter .item .item-title-container .item-title {
             color: #00aced;
         }

Дальше можно увеличить размер заголовка и убрать верхний регистр для текста:

/* Twitter */
.groupeditemspage .groupeditemslist .twitter .item {
           background-color: #012f41;
 }

     .groupeditemspage .groupeditemslist .twitter .item .item-title-container {
         background-image: none;
         height: 100%;
     }

         .groupeditemspage .groupeditemslist .twitter .item .item-title-container .item-title {
             color: #00aced;
             text-transform:none;
         }

     .groupeditemspage .groupeditemslist .twitter .item .item-overlay {
     }
     
         .groupeditemspage .groupeditemslist .twitter .item .item-overlay .item-date {
             color: white;
         }

В результате отображение будет примерно таким:

Попробуйте самостоятельно добавить к плиткам логотип Twitter. Или используя задание элементов сетки разным размером (см. статью http://msdn.microsoft.com/ru-ru/library/windows/apps/jj657974.aspx), привести отображение к примерно такому виду:

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

Детали группы

Прежде всего, стоит отметить, что во всех внутренних страницах (groupDetail и itemDetail) нам также доступна информация о типе исходных данных. Поэтому можно совершенно аналогичным образом добиться кастомизации отображения средствами CSS.

Ниже мы посмотрим другой вариант настройки, задав для этого отдельную страницу в проекте для контента из твиттера.

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

Чтобы понять, каким образом происходит переход к той или иной странице, откройте файл gropedItems.js. Найдите строчки:

navigateToGroup: function (key) {
    nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: key });
},

Функция на входе получает уникальных идентификатор группы (ключ) и далее делается переход на страницу groupDetails.html с передачей этого ключа в качестве параметра.

Давайте создадим новую страницу. Для простоты манипуляций (можно создать все файлы вручную, но так проще) скопируйте в проекте целиком папку groupDetails, поменяйте название новой папки и файлов на groupTwitterDetails.

Вернитесь в функцию navigateToGroup и поменяйте внутренний код на такой:

var group = Data.resolveGroupReference(key);

if (group.type === "twitter") {
    nav.navigate("/pages/ groupTwitterDetail/groupTwitterDetail.html", { groupKey: key });
} else {
    nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: key });
}   

Здесь мы через объект Data вытаскиваем по ключу информацию о группе (нам нужен тип) и далее в зависимости от типа переключаем пользователя на одну или другую страницу.

Замечание: обратите внимание, что вызов самой функции navigateToGroup прописан в нескольких местах, включая html-страницу – в событии onclick заголовка.

Теперь вернитесь к созданным страницам. Для начала, откройте groupTwitterDetail.html и поменяйте в заголовке ссылки на соответствующие файлы:

<link href="/pages/groupTwitterDetail/groupTwitterDetail.css" rel="stylesheet" />
<script src="/pages/groupTwitterDetail/groupTwitterDetail.js"></script>

Далее откройте groupTwitterDetail.js и обновите определение страницы с учетом переименования:

ui.Pages.define("/pages/groupTwitterDetail/groupTwitterDetail.html", {

Попробуйте запустить проект. Сейчас все группы выглядят по-прежнему одинаково, однако, группа для твиттера уже использует другие файлы.

Настройте внешний вид страницы на свое усмотрение, поменяв соответствующим образом стили и шаблон страницы:

Самостоятельно также создайте индивидуальную страницу для отдельного сообщения из твиттера. Не забудьте про настройку Snapped-режима.

< Онлайн-консультация 1 || Лабораторная работа 4: 12
Андрей Милютин
Андрей Милютин

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

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

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