close

Вход

Забыли?

вход по аккаунту

?

Win8JS

код для вставкиСкачать
Штефен Вальтер Создание приложений для Windows 8 с использованием HTML5 и JavaScr ipt УДК ББК У63 004. 738.5:004.4J avascript+ HTML5 32.973.202-018.2 У63 Штефен Вальтер Разработка приложений для Wiпdows 8 с помощью HTMLS иJ avaScгipt. Подробное руководство. Пер. с англ. Слинкин А. А. - М.: ДМК Пресс, 2013. - 344 с.: ил. ISBN 978-5-94074-921-9 Уже ocвoилиJavaSciipt и HTML? А Micгosoft как раз вооружила вас средства­
ми написания прорывных приложения для Wiпdows 8. Это уникальная возмож­
ность - а автор бестселлеров по веб-разработке для Wi пdows Штефен Вал ьтер покажет, как ей воспользоваться. В книге наглядно демонстрируются достоинства Wi пdows 8 с точки зрения веб-разработчиков. Подробно рассматривается новая библиотека Wiп J S, пред­
назначенная для написания приложений под новейшую версию Wi пdows. Вы узнаете о шаблонах JavaS c!'ipt, элементах управления и привязке к данным. В этой книге вы найдете подробное изложение самых разных вопросов: отображение данных в элементе ListView, работа с облачным хранилищем SkyD rive, создание игр, использование базы данных I пdexDB и форм HTML S. Автор рассказывает обо всем, что необходимо для разработки, тестирования и распространения выдающихся программ для Wiпdows 8, написанных с применением JavaS c!'ipt и HTM LS. Если вы занимаетесь веб-разработкой, то Wiпdovvs 8 может принести мил­
лионы потенциальных пользователей - и вы уже владеете многими навыками, необходимыми, чтобы заинтересовать их. Поку пайте эту книгу, овладевайте инс­
трументами и приемами, которые вам еще неизвестны, - и вперед на завоевание этого гигантского рынка! Oгigi11al Englisl1 language editioп puЬlished Ьу Peaгson Education, lпс" H.igl1ts <шd Contгacts Special Sales Depaгt111ent, U .S. Corpo1·ate and Goveгшпent Sales 1-800-382-
3419 (coгpsales@pearsontccl1gгoup.coш). Copyгight © 2013 Ьу Peaгson Educatioп, Jnc. H.ussi;ш-laпguage edition copyгigl1t © 2013 Ьу DMK Pгess. All гigl1ts гeseгved. Вес 11рава защи1.цены. Любая част�, этой 1ши1·и 11е может быть воспроизведена в l(al(OЙ бы то 11и было форме и l(аl(ими бы то 11и было средствами без письменного разреше11ия владельцев авторсl(их прав. Материал, изложе11ный в да1111ой l(ниге, многоl(ратно проверен. Но, гюс1юлысу вероятность техничес1<их ошибоl( все равно существует, издательство не может гаран­
тировать абсолютную ТО'ШОС'!Ъ и правилыюсть приводимых сведений. В связи с этим издательство не несет ответствешюсти за возмож11ые ошибl(и, связанные с ис1юльзо­
ванием 1шиги. JSI3N 978-0-672-33605-8 (англ.) ISBN 978-5-94074-921-9 (рус.) © 2013 Ьу Рса1·sоп Educatioп, !пс. © Оформление, псреrю11 на py cc l ( и i i Я:JЫI< ДМК llpecc, 2013 Посвящаетс я Аде Ризон Вальте р, кот орая т олько-т олько научилась читат ь (мои поздравления!). И которая хочет золот ую рыбку (я работа ю над эт им). И которая прЯАю сейчас лягнула свою ста ршую сестру Афину (прекрат и иемедлешю!) оrяАВЯЕНИЕ Об авторе .................................................. 12 Благодарности ........................................... 13 Нам важн о ваше мнение! ............................. 14 В ведение .................................................. 1 5 ГЛА ВА 1. Р азрабо тка приложений Магаз ина Windows ...................................... 18 Что такое приложение Магаз ина Windows?"." ... " ........ "." ... " .. 19 Принципы стиля оформления Майкрософт "" .. " ........ " .... " .. ".". 1 9 Общие характеристики приложений Магазина Wi ndows ...... " ..... 21 Соз даем первое приложение Магаз ина Windows""".""."""". 26 Созда ние проекта в Visual Studi o .... " ............................. "" ......... 27 Объявление возможностей приложения ........... " ........... "" ......... 28 Создание НТМL-страницы .................................. " ...... " ...... " ...... 30 Создание таблицы стилей ................. "." .......... " ........... " ........... 31
Созд ание Ja vaScri pt-фaйлa ............... "" ........... " " ... " ... "" .......... 32 Запуск приложения ....................... " .... "." ............... " .. "." .. "."." 34 Из чего сос тоит приложение Магаз ина Windows""""""""""" 34 Ja vaScript .......... " .............. " ....................................... "" ............ 35 HTML5 .. " ........... "." ........ " .......... " ... """ ..... ""." .. "." .. " .......... " .. 35 css 3 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 36 Среда выполнения Wi ndows .......... "." ............ " ............ " ..... " ..... 36 Библиотека Windows для Ja vaScript ............................................ 37 А что с jQuery? .............. " ................ " ...... " ..... " .............. " ........... 37 Пост роение приложе ния Магаз ина Windows в Visual Studio ...... 39 Шаблоны проектов приложений Магазина Wi ndows ................... 40 Запуск приложе ния Магазина Wi ndows ....... "." ........... "" .. " ... "." 47 Отладка приложе ния Магаз ина Windows.".""."" ...... "".""."". 48 Окно консоли Ja vaS cript в Visual St udi o ..... " .... """ ...... " ........ """ 49 Точки останова ......................... " ...................... " .. " ..... " ............. 50 Работа с обозревателем модели DOM ....... """"."." .... ""." .... " .. 50 Публикация в Магазине Windows "" .. "". "" .. " "" ."" ...... "." .. "" 52
Ре гистрация в качестве разработчика Магазина Wi ndows .......... 52 Отправка приложения ....................... " ....................................... 53
Сертификация приложения ................ "" ............ "." ................... 54 Резюме ................... " ... " ........... "." ..... " ......... " ... " .... " ... " ....... 56
Оглавление ...•• " ГЛА ВА 2. Ос новы WinJS ................................ 57 Прост ранства име н, модул и и классы .. " ... " ...... ".""."." ... "." .. 57 Прост ранства имен .................................................................... 58 Модули ...................................................................................... 61 Классы ....................................................................................... 63 Аси нхронное программи рование с обеща ниями ..................... 69 Обещания .................................................................................. 70 Сравнение методов then( ) и done( ) ............................................. 71 Создание обещаний ......................................... " .......... "" .......... 73 Созда ние обещания-таймаута ............................................ "." .. 74
Отмена обещания ...................................................................... 75 Композиция обещаний ............................................................... 75 От бор эл ементов DOM с помощью сел екторов за проса .......... 76
Выполнение запросов с помощью метода Wi nJS.Utilities.query( ) .................................................................. 77 Отбор одного элемента методом Wi nJS.Utilities.id( ) .................... 79 Использование метода Wi nJS.Utilities.chi l dren() .......................... 80 Работа с классом QueryCollection ............................................... 80 Выполнение Аjах-запросов с помощью функции xhr() .............. 82 Задание типа ответа .................................................................. 84 Зада ние свойств объекта Xml HttpRequest ............... " ................. 85 Резюме ....................................................... " .......................... 87 ГЛА В А 3. Набл юдаемые объ екты, привяз ки
и шаблоны ................................................. 89 Наблюдаемы е объе кты .......................................................... " 89 Создание наблюдаемого объе кта ............................................... 90 Создание прослушивателей наблюдаемых объе ктов .................. 92 Объединение уведомлений ................................................ " ...... 93 Обход отправки уведомлений .................. " ......................... " ..... 95 Работа с объектом Wi nJ S.Bi ndi ng.List ...................................... ". 96 Создание наблюдаемой коллекции наблюдаемых объектов ....... 98 Привяз ка к данным""" ... ""."."""""" .. " ... """""."".""""" .... 99 Де кларативная привязка к данным и наблюдаемые объекты .... 1 02 Получение соде ржимого НТМL-формы .......... " ........................ 1 04 Де кларативная привязка к данным и элементы управления Wi nJS ..................... " ...... " ...................................... 1 06 Декларатив ная привязка к данным и конвертеры привязки ...... 1 08 Ша блоны ........................ " ... "." ......... " .................................. 112
Императив ное создание шаблона .......... " ........................ " ...... 1 12 Де кларативное созда ние шаблона ......... " ................................ 1 14 Шаблоны и селекторы за просов ........................................ " ..... 1 16 Внешние шаблоны ................................... " ......... " ............. " .... 1 17 Резюме ................................................................................. 1 19 " ••.. � Оглавление ГЛАВА 4. Эл еме нты упра вл ения WinJS ......... 120 Введение в элементы управления WiпJS" ..... "" ... " .. "" .... " " .. 121 Декларативное создание элеме нта управления Wi пJS ............. 122 Императивное создание элемента управления Wi пJS .............. 1 24 Зада ние параметров элемента управления .............................. 1 25 Извлечение элеме нтов управления из НТМL-документа""""". 126 Эл емент упра вления Tooltip """ "" "". """" "" """" """ ".""." 127 Использование свойства coпteпtElemeпt."" "" """"" """ """". 128 Стилизация всплывающей подс казки" .... " ... "."" ... ".""""." .... 128 Эл емент упра вления ToggleSwitch.""."""" """"""""""""". 129 Определение состоя ния Toggl eSwitch """"" "" ." """" "" "". "" 130 Элемент управления Rati пg """""""" """"""."."""""" "" ". 131 Настройка элемента Ratiпg ... " ... " .. "."" .... ". " ..... "."."." .. "."." 132 Отправка оценки " ...... " ..... "." ....... "" ... " .... " ...... ""."."." ... ".". 1 32 Эл емент управления DatePicker" .. "".". "" """ "". ". """"""". 134 Форматирование даты .... "" .................. " ......... ". ". ".".". ". " .. " 135 Показ только годов, месяцев или дней ".""""" """""" """""" 137 Получение выбранной даты ... " .......... " .. """." ...... "."".".".".". 1 38 Элемент управления Тi meP i cker """"" """ "". ". "". """"""". 139 Получение и уста новка текущего времени".""""""""""""."" 141 Фор матирование часа, минуты и времени суток "" """." """". 1 42 Элемент управления FlipView"" " .. "." """. "."." "". """" """" 1 43 Отображе ние номеров страниц."""."""""""""""""""""""" 146 Создание нестандартных кнопок в элементе Fli pView ............... 1 48 Рез юме ... " .............. ""."" .... "" ........... " .... " ... "." ........ " ...... " 150 ГЛА ВА 5. Соз дание форм ........................... 151 Средст ва контроля данных в формах HTM L5""""""""""."". 151 Атрибут requi red .... " ... " ........ " ................ " .. " ...... ""." .... "." ... ". 1 52 Атрибут patterп "."." ... "" ...... " .......... "." ............. "".".". "". ".". 152 Нестандартный контроль данных " ... " ........... " .... " .. " ..... "" ...... 1 53 Настройка стиля сообщения об ошибке "."" """ """ "" ""."."" 1 55 Сброс формы ..... "." ..... "." ... " ..... ""."."."." .. "." ..... ""."""." ... 1 56 Эл ементы ввода да нных в HTM L5 """"""""". """" ." "" "."." 157 Метки полей формы "" .... " ....... " ... " .... ". " .. " .... """ " .. ". "".".". 1 58 Ввод чисел"."." .... " ... " .. " ..... "." ... " .. ""." .. " ... """""."." ... ".". 1 60 Ввод числа из заданного диапазона .... " ..... " ... "".".".".""" .. ". 1 6 1 Ввод адресов электронной почты, URL, телефонов и поисковых запросов "."""". """"" "". "."""""."."".".".""." 1 6 1 Ввод значения из списка ...... "" ......... "" .... " ...... ""." .... " .. "." ... 1 63 Выбор файла ......... " ..... " ............. " ... " .... " ..... " .... " ..... "." ... ".". 1 64 Созда ние редактора обогащенного текста ............................ 166 Показ хода выполнения ............. " ...... " ........... "." ........ " ...... " 167 Рез юме ..... " .... " ............... " .... " ............ " ..... "." ... " .... "." ... " .. 169 Оглавление ...• • " ГЛАВА 6. Меню и вс плывающие элеме нты .... 170 Эл емент управления Flyout .................................................... 171 Эл емент управления Menu .................................................... 173 Эл емент управления AppBar .................................................. 176 Создание простой панели приложения ....... "." ........... " ........... 177 Команды панели приложения ................................................... 179 Показ контекстно-зависим ых команд .... " ............. " .................. 182 Зад ание настроек приложения .............................................. 184 Создание страницы «О программе» ............ "" " .... " .... " ....... " .. 185 Создание персональных настроек ........................ " .................. 187 Ото бражение диал оговых окон Win dows ................................ 190 Рез юме ................................................................................. 192 ГЛАВА 7. Элеме нт управл ения Lis tView ......... 194
Введение в элемент управления ListView ............................... 195 Списковый и сеточ ный макет ....... "."."." .......................... "." .. 199 Предотвращение перекрытия элементов спис ка ListView ......... 201 Выбор элементов в списке ListVie w ........................................ 205 Создание предста вления «основной/подробности» ................. 207 Выбор нескольких элементов ................................................... 21 О Сорти ровка списка ListVie w ................................................... 213 Фил ьтрация спис ка ListView ................................................... 214 Группировка спис ка ListVie w .................................................. 216 Переключение предста влений с помощью контекст ного масшт абирования ................................................................. 219 Дин амическая за мена ша блона ListVie w ................................ 224 Постепе нная за грузка элементов списка ListView .................. 226 Рез юме ................................................................................. 229 ГЛАВА 8. Созда ние источников данных ......... 230 Соз дание неста ндартного источника данных ......................... 230 Создание класса источника данных ...... " .. " .............................. 231 Создание адаптера данных ...................................................... 231 Реализация метода getCount( ) ................................................. 232 Реализация метода itemsFroml ndex() ....................................... 232 Реализация метода insertAtEnd( ) .............................................. 234 Реализация метода remove( ) .................................................... 234 Реализация метода change() ....... " ........................................... 235 Обработка ошибок ...... "." ........................................................ 235 Реализация метода setNotificationHandl er( ) .............................. 236 Фа йл как источник данных ..................................................... 237 Использование файлового источника данных .......................... 239 Веб-служба как источник данных ........................................... 242 Создание исто чника данных ..................................................... 243 Оглавление Создание веб-служб ы ...................................... " ..... " ............ ". 244 Использование веб-службы как источника данных .... "."." ... " .. 246 База данных lndexedDB как исто чник данных ................. " ...... 247 Общие сведения о l пdexedDB ... " ... " .. " ..................... " .... " ..... ". 248 Использование источника данных l ndexedDB "" ........ " .. " ". """ 252 Рез юме ................ "" ...................................................... " ..... 258 ГЛАВА 9. Событи я и сост ояния приложения ... 259 События приложе ния .............. " .............. " ........... " ................ 259 Обработка события activated ... " ..... " ... " ........... " .. " ....... "."." ". 261 Обработка события error." .... " ....... "." ......... " .. ". " ................... 261 Откладывание событий с помощью обещаний """" .... " .. " .. " ". 263 Создание нестандартных событий ...... " .... " ..... " ....... " ........ " ... 263 Приоста новка, за верше ние и возобновление прил оже ния .... 264 Определение того, что приложение приостановлено или завершено ..... " .. "" ...... " ... " .. "." ... " ........ "." ..... " .... " ..... " .. 264 Определение предь�дущего состоя ния выполнения."""" ... "". 265 Тестирование состоя ния приложения в Visual St udi o ................ 266 Хранение состояния приложения в состоянии сеанса """. " ..... 267 Сост ояния просмотра приложения .................... " ........ "." ..... 269 Прикрепленное и заполняющее, альбомное и книжное .. " ........ 269 Опрос носителя."."." ..... "."." .. "" ..... " ............ " ................ " ..... 272 Использование метода medi aMatch в Ja vaScript .. """."""" .. "" 274 Определение окна просмотра .. " ....... " .... " ...... " .... " ... " ............ 275 Рез юме"." ............................................. " ............................. 278 ГЛАВА 1 О. Ф рагме нты ст ра ниц и навигация ... 279 Элемент управления HtmlControl .... " ....... "" .. " .. " .................. 279 Созда ние страничного элем ента управления ........................ 282 Созд ание многостраничных приложений" ........... " ... " ........... 285 Создание приложения с навигацией ".""" .. " .... " """ " ... """ "" 286 Страница default.html приложения с навигацией ... " ... "" ... """. 287 Добавление страничных элементов управления в приложение с навигацией ......... " ... " .... " .. " ...... ""." .... " ...... ". 288 Переход на другую страницу ................... " .............. "." ...... ".". 29 1 Структура API навигации " ................. " .... " ... " ..... " .. " .. ". " .. " .... 292 Элемент управления PageControl Navigator." "" ." ... " ..... " .. ". "" 293 Состояние навигации"." ...... " .......... " ........ " ...... "" .................. 293 Рез юме ........ " ...... "." ... " ...................... " ............................... 297 ГЛАВА 11. Р абота с Live Connect API ............. 298 Уста новка Live SD K .... "."" .... " ... " ...... "."" ........................... " 299 Добавление ссылки на Live SDK "." ......... "."" " ... " "" .. " ........... 299 Ре гистрация приложения ..... " ............. " ........ " ... "" .................. 300 Оглавление Инициализация Live Connect SDK ..... " ... " ................................. 301 Зада ние различных контекстов ........................................... "." 301 Аутентификация пользователя ................................... " ......... 304 Использование метода WL.login( ) ........... " ..... " ................... " .... 304 Использование элемента управления Si gnl n ... ". " .... " .. ".". " .... 305 События ауте нтификации ........ " ..... "."." ... " .................. " ......... 307 Переда ча маркера ауте нтификации веб-службе .................... 308
Отправка маркера ауте нтифика ции из приложения Магазина Wi ndows" ...... " ............................ " ........ " .... " ... "." .... 308 Проверка маркера аутентификации веб-службой." ... "." ...... " .. 311 Извлечение идентификатора пользователя .............................. 314 Получение основной пользовател ьской информации ............ 314 Скачивание и закачивание файлов в SkyDriv e ........................ 317 Получение списка папок и файлов в SkyDrive " .. " ...... ". " ... "."" 317 Скачивание файлов из SkyDrive .............. "." ..... " ...................... 320 Закачивание файлов в SkyDrive ... "."." .. " ......... ". " .... " ..... "."." 322 Рез юме ................................................................................. 323 ГЛАВА 12. Графика и игры .......................... 325 Опи сание игры ...................................................................... 326 Созда ние плиток для игры ..................................................... 327 Зв уковое сопровождение игры .............................................. 328 Созда ние холста для игры ..................................................... 329 Взаимо действие с пользователе м .. " ..................................... 331 Цикл обновления ................................................................... 333 Цикл рендеринга ................................................................... 334 Рез юме ................................................................................. 336 ПРЕДМЕТНЫЙ УКАЗАТЕЛЬ .........................337 О& А В ТОР Е Штефен Вальтер раньше работал старшим руководителем проектов в корпорации Майкрософт, а теперь владеет собственной компанией, оказывающей консультационные и образовательные услуги, - www. SupeгexpeгtTгaining.com. Он ведет практические курсы по созданию приложений Магазина Windows прямо на территории компании­
заказчика. Штефен заканчивал работу над кандидатской диссертацией в МТИ и там же вел занятия по метафизике, когда вдруг осознал, что метафизика никаких денег не принесет. Тогда он бросил учебу и принял участие в становлении двух успешных Интернет-стартапов. Он создал сайт Collegescape, которым более 200 колледжей, в том числе из Стэнфордского и Гарвардского университетов и из МТИ, пользуются для разработки онлайновых приложений для колледжей (позднее был продан компании ETS). Он также основал сайт City Auc­
tion - один из первых и самых крупных аукционных сайтов (позднее продан компании CitySeaгcl1). &ЯАrО ДАР НОСТ И Ох, писать техническую книгу - тяжкий труд, даже не помышляйте об этом! Я хочу выразить порицание своему редактору Нилу Роуи (Neil Rowe ), который уговорил меня взяться за очередную книгу. Хочу также упрекнуть свою жену, Рут Вальтер, которая не отговорила меня. И наконец, я обвиняю своего технического редактора Джеффа Бэртофта Oeff Buгtoft), который всеми силами стремился улучшить книгу и тем заставил меня потратить на нее еще больше времени. Честно - никогда и ни за что не соглашайтесь писать книгу ... НАМ ВАж.НО ВАШЕ МНЕН И Е! Вы, читатель этой книги, - наш самый важный критик и комментатор. Мы ценим ваше мнение и хотим знать, что мы сделали правильно, что могли бы улучшить, на какие темы нам стоило бы опубликовать кни­
ги. Да и вообще любые ваши мысли нам интересны. Мы будем рады вашим замечаниям. Можете писать нам по обыч­
ной или электронной почте о том, что вам понравилось или не понра­
вилось в этой юшге. А также о том, что мы могли бы сделать, чтобы наши книги стали лучше. Пожалуйста, имейте в виду, что мы не можем от вечат ь на те хни­
чес1ще вопросы по те. ме даююй ю-tu ги. Если будете писать, не забудьте указать название и автора книги, а также свое имя и адрес электронной почты. Мы внимательно изучим ваши замечания и передадим их автору и редакторам, работавшим над книгой. Электронная почта: consumeг@samspuЬlishiвg.com Почтовый адрес: Sams PuЬlishiвg ATTN: Readeг Feedback 800 East 96t1' Stгeet Indiaвapolis, IN 46240 USA В помощь читат еля м Зайдите на наш сайт и зарегистрируйте свой экземпляр книги по ад­
ресу www.infoгmit.com/гegisteг, чтобы получить доступ к обновлени­
ям, загружаемым материалам и перечню замеченных опечаток. - -
ВВ ЕДЕНИ Е Если вы разрабатываете программу, чтобы охватить максималь­
но широкую аудиторию и выручить как можно больше денег, то имеет смысл задуматься о приложениях для Windows 8. Micгosoft Windows - самая популярная операционная система в мире. На долю Windows приходится свыше 90 % рынка операционных систем. По состоянию на июнь 201 2 года было продано более 600 миллионов ли­
цензий на Windows 7. По сравнению с рынком Windows все осталь­
ные рынки программного обеспечения (включая приложения для i Phone и Android) кажутся карликами. Да, не все пользователи предыдущих версий Windows перейдут на Windows 8. Но можно с большой долей уверенности предположить, что их будет очень много. Генеральный директор Майкрософт Стив Балмер - согласен, не самый непредвзятый человек - предсказывает, что к концу 201 3 года на Windows 8 будут работать более 500 мил­
лионов пользователей. Лично я хочу иметь золотой унитаз, самолет, как у Натана Мирволда, и спортивный электромобиль Tes]a Roadsteг (оранжевый). Довольно скромные пожелания, уверен, что многие чи­
татели этой книги хотят того же. А достичь этих целей вы и я (лучше я) можем, разрабатывая приложения для Windows 8, - это самый пер­
спективный способ. Написанное приложение для Windows 8 можно прямо из Windows 8 и продать. В состав Windows 8 входит Магазин Windows (Windows Stoгe), где вы можете выставить плоды своих трудов по цене от нуля до 999,99 долларов. Продавать можно самые разные приложения - от повышающих продуктивность (вспомните о программах учета вре­
мени и управления контактами) до игр (таких, например, как AngJy Вiгds или Cut the Rope ). Эта книга посвящена приложениям Магазина Windows, то есть таким, которые можно продавать через этот магазин. Конкретно, вы научитесь писать приложения Магазина Windows Stoгe с помощью JavaScгipt и HTML5. Почему именно JavaScгipt и HTML5? Создавать приложения Ма­
газина Windows можно и на языке С# в сочетании с XAML или на " • • ••• * Введение С++, но в этой книге мы будем говорить исключительно о разработке нaJavaScгipt и HTMLS. Store Spot!iql't • Рис. В. 1. Магазин Wi ndows Store Преимущество такого подхода в том, что можно применить опыт создания веб-сайтов к разработке приложений для Windows. Если вы уверенно владеете JavaScгipt, HTML и каскадными таблицами сти­
лей, то создание приложений Магазина Windows не вызовет затруд­
нений. В эт ой кни ге рассказан о обо всем, что необходимо для разработки приложений Магазина Windows. Вы научитесь пользоваться библи­
отекой Wiнdows дляJavaSCiipt (Wi нJS), предназначенной для разра­
ботки приложений нa JavaScгipt, и, в частности, узнаете о таких эле­
ментах управления WiнJS, как Rating, Menu и Li s tView. Кроме того, вы узнаете, как работать со средой выполнения Windows (Windows Ruнtime). Эта среда открывает доступ к таким возможностям Windows 8, которые нельзя реализовать в веб-прило­
жении иными средствами, например захват видео и звука. Прочитав книгу до конца, вы будете понимать, как пишутся при­
ложения Магазина Windows - в частности, игры и программы повы­
шения продуктивности. Так, в главах 7 и 8 мы с помощью элемента управления Li s tView создадим простое приложение для ведения списка задач. А в главе 1 2 н апиш ем несложную аркадную игру «По­
жиратели мозга». Введение 111 • •1FJ1 Прочтите эту книгу, создайте приложение Магазина Windows, продайте побольше копий и купите себе реактивный самолет. Необходимые предв аритель ные усло вия Если вы умеете создавать веб-сайты с помощью JavaScгipt, HTML и CSS, то обладаете всеми навыками, необходимыми для чтения этой книги. Для разработки приложений Магазина Windows и использования приведенного в книге кода должны быть выполнены два условия. Во-первых, приложения Магазина Windows создаются в операци­
онной системе Windows 8. Повторю еще раз: чтобы выполнить при­
меры кода из этой книги, на вашем компьютере должна быть установ­
лена Windows 8. Во-вторых, вам понадобится Micmsoft Visual Studio 201 2. С сайта Micгosoft.com можно скачать бесплатную версию Visual Stl!dio 201 2 -
Micгosoft Visl!al Stlldio 201 2 Expгess для Windows 8. Примечание. Если строчка кода превышает ширину печатной страни цы, то в конце ставится знак продолжения ('+). Исходный код Весь исходный код к этой книге можно скачать с сайта GitHub по адре­
су https: / /gi thub. com/StephenWal ther /WindowsBAppsUnleashed. Для получения последней версии кода в виде ZIР-файла щелкни­
те по ссылке Downloads. - - -
rЯАВ А 1. Р аз ра б отка nр иnож ен ий Ма rази на 1"'indows В этой главе: • Что такое приложение Магазина Wiнdows?
• Создаем первое приложение Магазина Wiнdows.
• Из чего состоит приложение Магазина Wiнdows.
• Построение приложения Магазина Wiнdows в Visual
Studio.
• Отладка приложения Магазина Wiнdows.
• Публикация в Магазине Wiнdows.
В этой главе мы познакомимся с основами разработки приложений Магазина Wiнdows. Для начала я объясню, чем приложение Магази­
н а Windows от лич ае т ся от т ра дицио нного прил ожения для настоль­
ного ПК Вы узнаете, благодаря чему приложение становится прило­
жением Магазина Wiнdows. Не ведая страха, не опасаясь преград и в надежде, что вы тоже ни­
чего не боитесь, я далее проведу вас по пути создания вашего перво­
го приложения Магазина Wiнdows. Вы узнаете, как воспользоваться Miciosoft Visual Studio 201 2 для сборки, запуска и отладки приложе­
ния Магазина Wiнdows. Затем мы перейдем к обсуждению основных элементов прило­
жения Магазина Wiнdows. Вы узнаете, как оно куется из HTML5, JavaScгipt, библиотеки Windows для JavaScгipt и среды выполнения Windows. Наконец, мы перейдем к финансовой части. Я объясню, как опуб­
ликовать приложение в Магазине Windows и начать зарабатывать денежки. Что такое приложение Магазина Windows? ...•• " Что та кое приложе ние Магаз ина Win dows? Я до сих помню, как первый раз воспользовался i Phone. Когда на «ай­
фоне» прокручиваешь экран, изображение буквально подпрыгивает! А когда отправляешь письмо в корзину, так оно туда всасывается! Как будто внутри «айфона» существует крохотная Вселенная, живу­
щая по привычным нам физическим законам. По какой-то причине - которую я не вполне понимаю и до конца не разбирался - иллюзия второй Вселенной в моем «айфоне» достав­
ляет мне радость. Из-за этого иметь дело с «айфоном» - сплошное удовольствие. Но вернемся к Windows. Если не считать хоровода карт в пасьянсе «Солитер», не могу припомнить, чтобы при работе с Windows у меня возникало такое же ощущение веселья. Не помню, когда в последний раз Windows заставляла меня смеяться или прыгать от радости. Но приложения Магазина Windows доказывают: Майкрософт на­
конец-то признала, что впечатления пользователя от работы с систе­
мой - далеко не последнее дело. В основе этих приложений лежит ряд положений, получивших название принципы стиля оформления Май­
крософт. Руководствуясь этими принципами, вы сможете создавать более «живые» приложения, с которыми приятно работать. Принципы стиля оформления Майкрософт Принципы стиля оформления Майкрософт - это совокупность принципов организации работы пользователя, которые корпорация Майкрософт разработала в контексте создания приложений для Windows Phone, ХЬох Live и приснопамятной Zнne. Практическое воплощение этих принципов можно увидеть на таких сайтах, как Micгosoft SkyDгive и Windows Аzше Poгtal. Готовы? Читайте. 1. Будьте искусными в деталях. Уделяйте время и силы мелочам, которые часто видны мно­
гим. Все этапы работы с продуктом должны проходить без сучка без задоринки. 2. Достигайте большего меньшими средствами. Устраняйте отвлекающие факторы, не стремясь все разже­
вать. Если человек с головой погружен в то, что ему нравит­
ся, то исследованием он займется сам. IEIJ •• •lfi Глава 1. Разработка приложений Магазина Windows
Делайте интерфейс ясным и целенаправленным, оставляй­
те на экране самые важные элементы, чтобы пользователь мог погружаться в содержимое. 3. Стремитесь к быстроте и подвижности. Пользователи должны взаимодействовать непосредствен­
но с содержимым, а программа - реагировать на их дейс­
твия быстро и энергично. Вдохните жизнь в программу, создайте ощущение непре­
рывности, пусть движение говорит само за себя. 4.Будьте верны цифре.
Пользуйтесь всеми преимуществами цифровой среды. Ус­
траняйте физические барьеры, стремитесь, чтобы работа была эффективнее и требовала меньше усилий, чем в мате­
риальном мире. Представьте, что все мы - пиксели на экране. Вносите в оформление смелые, яркие, необычные цвета и картинки, выходящие за рамки унылой реальности. 5.Сила - в единстве.
Пользуйтесь экосистемой, интегрируйтесь с другими при­
ложениями, устройствами и операционной системой, что­
бы ничто не повисало в воздухе. • Встраивайтесь в модель пользовательского интерфейса, чтобы устранить избыточность. Учитывайте то, что пользо­
ватель уже знает, это создаст у него ощущение привычнос­
ти, уверенности и владения ситуацией. Примечание. Принципы оформления Майкрософт раньше назывались принципами оформления Metro. Приведенный выше перечень взят из ста­
тей, опубликованных по адресам http://msdn.mi crosoft.com/en-us/l i brary/ wi ndows/apps/hh464920 и http://msdn.mi crosoft.com/en-us/l i brary /wi ndows/ apps/hh465424.aspx. Когда я впервые прочитал эти принципы, они показались мне чрезмерно абстрактными и расплывчатыми. Как раз такими, какие и должен был изречь парень в берете, представляющийся спецом по удобству работы. Но увидев, как эти принципы применяются на практике - в ходе разработки настоящих приложений Магазина Wiвdows, - я взглянул на них по-новому и с большим уважением. Взять, к примеру, принцип «Достигайте большего меньшими средствами». Одна из отличительных особенностей приложения !'viа­
газина Wiвdows - отсутствие обрамления окна. По иронии судьбы, Что такое приложение Магазина Wi ndows? .. " приложение Магазина Windows -это приложение Windows без Win­
dow (окна). Все приложения Магазина Windows полноэкранные. Отсутствие обрамления позволяет сосредоточиться на содержи­
мом приложения. Например, в Windows 8 есть две версии Inteгnet Explшer: настольная и версия для Windows 8, бескомпромиссно сле­
дующая принципам оформления Майкрософт. Честно говоря, вторая нравится мне больше. При работе с ней видна только веб-страница, которая и составляет главный смысл приложения. Или возьмем принцип «Стремитесь к быстроте и подвижности». Почему мне так нравится работать с «айфоном»? Потому что он со­
здает иллюзию движения - благодаря продуманному использованию анимации. На «айфоне» объекты дрожат и подпрыгивают. Все средства разработки приложений Магазина Windows прямо­
таки подталкивают к использованию анимации. Например, в стан­
дартном элементе управления ListView (мы будем подробно изучать его ниже) операции добавления и удаления элементов анимированы. Удаленный элемент не просто исчезает - элементы выше и ниже него сближаются, занимая его место. Примечание. Существует технический термин для обозначения компьютер­
ной имитации физической реальности -скеоморфизм. Общие характеристики приложений Магазина Windows Приложениями Магазина Windows называются приложения, следу­
ющие принципам оформления Майкрософт. Кроме того, приложе­
ния Магазина Windows рассчитаны на исполнение в операционной системе Windows 8 или Windows RТ. У всех приложений Магазина Windows есть общие характерис­
тики. Я расскажу о них на примере версии Inteгnet Exploгer для Windows 8. Подд ержка клавиатур ы, мыши, касан ия и ст илоса Одна из бросающихся в глаза особенностей приложения Магази­
на Windows - увеличенные плитки и кнопки и обилие пустого места. Благодаря такому «просторному» пользовательскому интерфейсу приложения Магазина Windows удобны для людей с толстыми пальцами. " •••• "' Глава 1. Разработка приложений Магазина Wi ndows С приложениями Магазина Windows одинаково комфортно рабо­
тать на планшете, не имеющем ничего, кроме сенсорного экрана, и на настольном ПК, оборудованном клавиатурой и мышью. Приложения Магазина Windows пишутся так, чтобы их было легко и интересно исследовать. С точки зрения разработчика Windows 8 хороша тем, что не нужно задумываться о поддержке касаний. В стандартные элементы управ­
ления WiвJS поддержка клавиатуры и касаний уже встроена. Панел ь прилож ения и панел ь навигации На рис. 1.1 показана версия Inteшet Ехрlогег для Windows 8, в котором открыта начальная страница сайта газеты Нью Йорк Тайме. Обратите внимание, что видно только содержимое сайта. Нет ни панели инструментов, ни кнопок, ни строки состояния . ... ......................................... ..! "".,..: !."," .... ' :,.._.._ i1._...,... :,;.,,," =�Ф''"°''>' =�-.""­
!с.,," ....... ". il)o<w>.,/.\'д""' i(!·, ... -
=".,..,".r,�,," ;"",.,......,.,'1-М<' ;"�....,�(:.o>hn � ·'"'"' ''".>Я"-""·• !1,1,",.., .iJ сдмР ... 1сJ>1 ao.u Psн-tisn1.1 R.if\s Hiн1:111.'"1• li:fГ01·1.-. t.o 111-1vro,,c tr.S. ;-::��-��.��� �,�;-rtaш "l'И':'!'-·"���1� �ltм- �!сч>-� �;:����c:1d�"�1:;1:i s"r.rN\'l<'Ct\llrl n•Hn.�Ath� t:EiJ!M SU>t��· •Y:!i11r "'�!t:'><I• a�••!Wнt•"·1rbprnbltoш» Toen 1•"•·1·,, t-·"�n1·11.., '·' · ··� '-· \\."!•"" т ... '-:,._" R>oнuff '\"ltlo 1R l\1•·tl1tl.". (;1""_.,.l•on N". 09 (,". Plo""ll'>< "'"'' '""'"'""'�--..... ,., •. ,,."_;•)to.'·<'�<l�:-<l""«tl r...i \n1,•.\J�r.:..1"11ц'ift 11,.. ео�•-1 1..,.,t,.. ''"'""i."' '"'"" 01.�""i. .....,,.,,1 !l>f 11 ... t),....·J,t:1Jl. li.нo c;шdi41w �ж t0n!on LI\ •о6-1; м1сh��1 rh1lpt b101:»1t«1 t'UtIOJ.)·. '�'"""' \>� r"" .. JUfk у�"''•;" ", • ..,". t.>lj)·11м "1111""'"''"'"'" 1�"щ ltn<t!, ;.;::�;:;�;n���:llt.1>n · Q r<>" • � ... ..,.""" , ��..., \ 1111 • 1>J ... 1p�;;.,1" R.....,�,, \•'111• .,1" мм"••=----·�� Рис. 1. 1. Wi ndows 8 l nternet Explorer В приложении Магазина Windows все команды скрыты в пане­
ли приложения. Строка приложения появляется в результате жеста прокрутки от нижнего или верхнего края экрана или щелчка правой кнопкой мыши по экрану. На панели приложения Iнtегвеt Ехрlогег находится адресная строка и различные кнопки, в частности «Назад» и «Обновить» (рис. 1.2). Что такое приложение Магазина Wi ndows? "'"""� ...... " '•t.'ЯXLI• ;,,,; ............. " !'ПE.CtalOl.OC\I ,..-l'OПS !�.,,. ;""",.." !....__ !п.,,.� ,_, !("�-А"' � с�.м...,....., !F.:��:" i"""""'"'�­
!r" •• ,,.."$ $:,i,; [ 1«-... "t..c:».lk« • CAMPAIGN 2012 P"н·tis"п Rif\� J·Ii11c:le1· 1::ff o1·ts to 11нр1·оус tf'.S. У.1(�.�!,��-�_,��:�· (.�111 '1'"'"1�-� )��" :o.ll,r11 t<'«'-�k>'!"' tl>-'<'-'llpf�•�if'"t;.ilto.::i:"..-tw. n.:..Ш:.,..,d..J"'"d."1-Jн• �1>«:'1...-г�..,rr nit,11i,1tu­
l.'11i!IO $\�",,.. �<ltiUJ; a::�hod.> ).r"as l..l.d"')"'�llt)>l"PЬl�щ.,;•� ...• • " Рис. 1.2. Панель приложения и панель навигации Обратите внимание еще на одну панель в верхней части экра­
на. Это панель навигации. В случае Internet Ехрlшег она содержит миниатюры всех открытых в браузере вкладок. Панел ь чудо-кнопок Если провести пальцем от правого края экрана к центру или под­
вести мышь к любому из двух правых углов или нажать клавишу Win+C, то появится панель чудо-кнопок (рис. 1.3). Приведем перечень стандартных чудо-кнопок: Search ( Поиск) - позволяет искать в содержимом текущего и других приложений. Share (Общий доступ) - открывает доступ к содержимому те­
кущего приложения со стороны других приложений. Start ( Пуск) - переход к начальному экрану. Devices (Устройства) - подключение к устройству. Settings ( Параметры) - задание параметров приложения и системы. Эти чудо-кнопки - стандартные места для размещения типичных функций приложения. Например, все параметры приложения Магази­
на Windows должны быть доступны с помощью чудо-кнопки Settings (рис. 1.4). Так пользователю гораздо проще искать настройки. llEIJl•• • l �I Глава 1. Разработка приложений Магазина Wi ndows " " � Ьо""f'"�:Ф• J'CU N't'\\' °f'f)IJo; ·iн lJFtllftgL'1>11ШI S!:•ft·�Яl 1H\\' �:J�)�!;�� :,�;��'�':� "·.;; .. -·;'� •� Ш/> ThfW.<�y:,- �t:UI� t>m<К;, �""� ��·:"!х;�\ •;>>j;'J �� J:<!C.N.=, �U,Цt :�1;'•1��­
l.-��- )';р�ф. ;::.... ...... 1 �-��'<''>•\\'i�i:'I Ш11'.f'"-ц'41:t�! мтr�,-..ш - ��;:;:,:;;���" \01-;,й;<'.-с_...,)"�_..«tс'\.1') 1;i 1 Q · 3 7 Wednesday • August 1 Рис. 1.3. Панель чудо-кнопок N(.'\\'Pcl l� iн B11lllt•gro1шtl .Nl:tlf'!>N!ш''' ��!�,�;;�·����7,�. � .... ",,,,,,.., .... !,�Jr.! ,..,..._,�-d,_ ... ,4i'l""ol ���-f':.���&.-l:U. ���:-��ж ��-а�--�"�=� ��_,.,,,щ.:><! ft;щi;!\J;":;,., ·""'""'"'""'"r� �-'<;Jh_ _",,.,.:;с;�· ... :;� 111'�.v-,.,......,,,.,,.",_�; t.1PH>c<� �\tj} К'<.."V'.жl'><}·� �.-. 'IJU::>-11°!"-1.n:t";i.t,U.VltUC'<<f'l'.J<l:-..'l:ftlO � @��:С';.� �:.:::���:f_atf>:1'J IMll\':Щ$ .OI�""-,,� ,;,�l)"�!h.rr:t """� �·\}-А �!fJrn'I �!!&tЩ! n�(!<!. '\')1�ro:i1u·1�"� Рис. 1 .4. Чудо-кнопка Settiпgs Что такое приложение Магазина Wi ndows? ...• • " Разработчик приложения Магазина Windows не создает собствен­
ное меню параметров. Вместо этого он добавляет параметры прило­
жения в чудо-кнопку Settings. Как это делается, мы подробно обсу­
дим в главе 6 «Меню и всплывающие элементы�. Сост ояния - полноэ кранное, прикрепленное и за полня ющее Приложение Магазина Windows может находиться в одном из трех сост ояний просмот ра: полноэкранное, прикрепленное и запол­
няющее. По умолчанию приложение занимает всю доступную пло­
щадь экрана (полноэкранное состояние). Для переключения в другое состояние следует нажать клавиши Wi n +. (точка) или провести пальцем от верхнего к левому краю экра­
на или буксировать мышь от левого верхнего угла. Предупреждение. В прикрепленное состояние приложение можно пере­
вести только на устройстве, для которого разрешение по горизонтали не менее 1 366 пикселей. В противном случае на экране просто не хватит места, чтобы одновременно увидеть больше одного приложения. На рис. 1.5 изображены два приложения Windows 8 на экране. Приложение Weatl1eг ( Погода) находится в прикрепленном, а прило­
жение Inteгnet Ехрlогег - в заполняющем состоянии. Ширина при­
крепленного приложения составляет 320 пикселей. Заполняющее приложение занимает всю оставшуюся площадь экрана. 11Vf.l•ltJ(l'J1C.1t!� 'fl::i�f,_·� l'>A 1"t•�� ltlr>rkl�)· ... -t�r �,·�11мr.r�i :;;:-�,., ..... <!;,j. <o�#··" • • -, .... ...w .... А�4> �� .... �� ... _ ........... _ �"н;:;""';.:,"t.;..�· ... �.<".-:lto...:;"\-'. ,",<;..,.>,J,.,.,_ �·:::z;:.;,:-• l:W.... ... �l1c �r\u !}ork {!;i11ш1 ........ Чs:l'-1"!."<> -�·-=-
)'·_:�11:::3 �f .... §
�.;{:; :·:
::
::::
::::
-
11.н
1iii:;.
,;;�� . · ······1 · �.:. __ , __ ·"'· '""-'" ·-. 4 WEEKS �:t.<"'!1· � 1:..,;."-.. �.\.�·;#м� � <.: � '··• 9. ф Рис. 1 .5. Прикрепленное и заполняющее приложение 1El:1•8 11i Глава 1. Разработка приложений Магазина Windows Проектировать приложение Магазина Windows следует так, что­
бы оно было работоспособно в любом состоянии - полноэкранном, прикрепленном или заполняющем. В любой момент ширина занятой приложением области может измениться. О том, как обрабатывать переключение между состояниями просмотра, я расскажу в главе 9 «События и состояния приложения�>. З акрытие приложения Магаз ина Wi ndows Теперь закройте приложение Магазина Windows, наведя курсор на значок х в правом верхнем углу экрана. Ха! Купились? Нет в при­
ложениях Магазина Windows кнопки закрытия, потому что обрамле­
ния нет. Примечание. Закрыть приложение Магазина Wi ndows можно, хотя интуи­
тивно неочевидно, как это сделать. Для этого нужно провести пальцем с са­
мого верха до самого низа экрана или нажать клавиши Al t+F4. То, что не существует очевидного способа закрыть приложение Магазина Windows, не случайно. Предполагается, что вместо закры­
тия вы просто переключитесь на другое запущенное приложение (проведя пальцем от левого края экрана к центру) или запустите но­
вое (выбрав его на начальном экране). Проектируя приложение Магазина Windows, вы должны помнить, что пользователь может в любой момент покинуть ваше приложение и снова вернуться в него. В главе 9 я покажу, как корректно возобно­
вить работу приостановленного приложения. Соз даем пе рвое прилож ение Магаз ина Wind ows Оставим страхи. В этом разделе я проведу вас по всей процедуре со­
здания приложения Магазина Windows. Приложение «Здравствуй, мир�> слишком предсказуемо и скучно. Поэтому я предложу кое-что поинтереснее. Мы создадим приложение, которое будет делать фотографии. На­
жав кнопку Take Pictшe (Сфотографировать) на панели приложения, вы сможете сделать снимок, который затем отображается на экране (на рис. 1.6 показана фотография моего пса Ровера). Примечание. Полный код приложения находится в папке Cl1apter1; само приложение называется Арр1. Создаем первое приложение Магазина Windows Рис. 1.6. Ваше первое приложе ние Магазина Wi ndows Создание проекта в Visual Studio Первым делом мы должны создать проект в Miciosoft Visual Studio. Почти все примеры из этой книги созданы в Visнal Stнdio 201 2. Как правило, я использовал бесплатную версию Visual Studio 201 2 Ex­
piess для Windows 8, которую можно скачать с сайта Micгosoft.com. Примечание. В тех главах, где создаются проекты веб-служб, я использо­
вал издание Vi sual Studi o Professi onal 201 2, потому что в одном решении мне было необходимо и приложение Магазина Wi ndows, и приложение ASP.NEТ. Создавать приложения Магазина Wi ndows можно как с помощью Mi crosoft Vi sual Studi o 201 2, так и с помощью Mi crosoft Bl end. Если вы собираетесь пуб­
ликовать его в Магазине Wi ndows, то я рекомендую использовать Mi crosoft Vi sual Studi o 201 2. Для построения приложения Магазина Wi ndows необхо­
димо установить Vi sual Studi o на компьютер с ОС Wi ndows 8. Если физичес­
кого компьютера с Wi ndows 8 нет, то можно воспользоваться виртуальной машиной, например с помощью программы VMware Pl ayer. Запустите Visual Studio. Выберите из меню команду File � New Pгoj ect ( Файл � Создать проект). В левой части диалогового окна New Pгoject ( Новый проект), выберите язьшJаvаSсгiрt, а затем шаб­
лон Вlank Арр ( Пустое приложение). Назовите проект Арр1 и нажми­
те кнопку ОК (рис. 1.7). "' lnstalled _. lemp1i11trs _. Ji!l\'i!IScript \Vindows Stor� LightSwitch 1• Othrr languag� V Othrr Proje.d lyprs ModtlingProj� SampJe.s Глава 1. Разработка приложений Магазина Windows New Project .NET Framr:·.vork 4.5 � Sort Ь}': Dtfault S:] BlankApp J11\•11Script r:;-.J Grid Арр J.JvaSciipt kJ SplitApp Jll\'.JScript r:.'J Fi�rd l11yout дpp J.J<n1Script ,_.i� DDJ Na<zigi!ltio n A pp Jt1v.1Scripl Туре-: J.tvllScript А 'iingt�pagt- projнt for а VJindaw.s Stare. арр lhat has па pre.ddine.d cantrols or layout. Location: C:\Usrл\Ste.p he.n\Documr:nU\MrtroH1ML 5J�vaScriptUnle.ashe.d\Chl � � Solution namr; Appt � Cre11te. dirr:ctoryfor solution О Add to sourct' control г······ак······ш··1� Рис. 1. 7. Диалоговое окно «Новый проект» в Visual Studi o После создания проекта можно просмотреть все принадлежащие ему файлы в окне Solution Ехр!огег ( Обозреватель решения) (рис. 1.8). В новом приложении Магазина Windows есть файлы default.html (в корне проекта), default.j s (в папке js) и default.css (в папке css). С этих трех файлов и начинается приложение. � '0 · .-:t@J 8il i� 5r:ar'h Sol\1tii:'n E.:.;pl:;;:1�r (Ctrl"·;} Р " '4J Solution 'Арр1' (1 proiect) ow··Wr;i� '" •· 8 Refl!!rences: А iiJ С5� [21 default.cs§ iil images � iaJj> [J defaultJ> ,\3 Арр1_ Т ompor aryKey.pfx .а d�fault.htn11 � package:.appxmanifШ Рис. 1.8. Фай лы, созданные по умолчанию для приложения Магаз ина Wi ndows Объявление возможностей приложения Перед тем как приступать к написанию кода, мы должны сделать еще одну вещь. Мы собираемся создать приложение, которое будет фотографировать. Это опасно. Теорепиески приложение могло бы Создаем первое приложение Магазина Wi ndows •• &11 фотографировать без вашего ведома и отправлять снимки злобному хакеру, рыскающему в Интернете (или Стиву Балмеру, сидящему за своим столом в Майкрософт). Если приложение делает что-то опасное, вы должны заранее объ­
явить о своих намерениях и получить согласие пользователя. Воз­
можности приложения объявляются в его файле-манифесте. Чтобы открыть редактор манифеста, дважды щелкните по файлу package. appxmanifest в окне обозревателя решения. Перейдите на вкладку CapaЬilities (Возможности), где показа­
ны все объявленные возможности приложения. Например, если вы хотите, чтобы приложение могло записывать звук с микрофона, то должны отметить возможность Microphone. А если приложение должно сохранять новые фотографии в пользовательской библиоте­
ке Pictures, то нужно отметить возможность Pictures Libгary. В нашем случае требуется отметить возможность Webcam, чтобы разрешить приложению фотографировать (рис. 1.9). Appl i cation UI Capabilitfrs: О Documf:nts library О fnterpri�e Authenticat.ian � lnternf.t (CHe.nt) О lnternEt: {Client & Se.rverJ О locati on О Microphone О Music libraгy О Pictur� l i brary (llpahili ti� О Private Netwш � (Client &. Seг..rer) О Proximity О Re.movaЫe Storage О Sharl!d U!ier Certific.attl О Video� Library RJ \�Jebo1m Declarations O�rtptIOn: Pro'-;ides ассв;;tо coпnectcd �t.Ь:.:,з1 Mvro:; i 11fантн1ti:щ Рис. 1.9. Разрешение делать фотографии Когда пользователь в первый раз запустит наше приложение, сис­
тема спросит, можно ли разрешить ему доступ к веб-камере (рис. 1.1 О). Пользователь должен дать разрешение только один раз. IEПl• • ll l C Глава 1. Разработка приложений Магазина Wi ndows Рис. 1. 1 О. Запрос разрешения на доступ к веб-камере Примечание. Однажды дав разрешение на использование некоторой возможности, пользователь затем может отозвать его, воспользовавшись вкладкой Permi ssi ons ( Разрешения) в чудо- кнопке Setti ngs. Создание НТМL-страницы В корне вновь созданного приложения Магазина Windows находит­
ся файл default.11tml. Это страница, которая открывается сразу после запуска приложения. Изменим ее в соответствии со спецификой на­
шего приложения (листинг 1.1 ). Листинг 1.1. Модифи цированная страница defaul t.html <!DOCTYPE html > <html> <head> <meta charset ="ut f - 8" /> <t i t le>Appl</t i t le> <!- - Ссылки на WinJS - - > <l ink href = • //Mi crosoft.WinJS.1.0/css/ui -dar k.c s s • rel ="s t yl esheet" /> <s cript src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <script src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Appl - - > <l ink href ="/c s s/def ault.cs s • rel ="s tylesheet" /> <script src= •/j s/def ault.j s"></script> </head> <body> <img i d="imgPhoto" src="/images/pl acehol der.png" /> <!- - Эле мент уп равл ения AppBar - - > <div i d="appBarl" Создаем первое приложение Магазина Windows � ..• • " data-win-control ="WinJS.UI.AppBar"> <button data-win- control ="WinJS.UI.AppBarComrnand" dat a-win-opt i ons ="{ i d: 'cmdTakePi cture' , l abel:'Take Pi cture', i con: 'camera', tool t ip:'Take Picture' }"> </button> </div > </body> </html > Мы включили в тело страницы новое содержимое. Во-первых, об­
ратите внимание на тег I MG с идентификатором imgPhoto. Именно здесь будет показана отснятая фотография. Далее, отметим тег DIV с атрибутом data-win-control="WinJS. UI .AppBar". Это пример элемента управления WinJS - он отрисовы­
вает панель приложения, на которой находится команда фотографи­
рования (рис. 1.11 ). Рис. 1. 1 1 . Команда Take Picture на панели приложения Создание таблицы стилей При создании нового приложения Магазина Windows вы по умолча­
нию получаете в папке css таблицу стилей default.css. Этот файл со­
держит правила, описывающие внешний вид приложения в различ­
ных состояниях просмотра. Примечание. Подробно состояния просмотра приложения Магазина Wi n­
dows обсуждаются в главе 9. Для нашего приложения я внес в файл defaнlt.css изменения с целью отформатировать фотографию: #imgPhot o { display : Ыосk ; margin : 1 5рх aut o 11EPJ •• •llJ Глава 1. Разработка приложений Магазина Wi ndows border : 1 0рх s ol i d whi t e ; max-width : 9 0 % ; max-hei ght : 9 0 % ; Создание JavaScript-фaйлa Теперь нам предстоит модифицироватьJаvаSсгiрt-файл default.js, ко­
торый находится в папке js. Этот файл содержит весь код, относящий­
ся к странице default.html. Мы полностью заменим содержимое этого файла. Новый код по­
казан в листинге 1.2. Листинг 1.2. Файл default.js funct i on ( ) "use stri ct" 1 1 Псевдонимы var capture = Windows.Medi a.Capture; 1 1 Выполняется сразу после загрузки содержимого страницы funct i on init ( ) { 1 1 Обработать все элементы управления WinJS.UI.proces sAl l ( ) .done ( funct i on ( ) 1 1 Ссылки на элементы DOM var cmdTakePi c ture = docu ment.get El e ment B yi d ( "c mdTakePi ct ure" ) ; var imgPhoto = document.getElementByid ("imgPhot o"); 1 1 Обработать щелчок по команде Take Pi cture cmdTakePicture.addEventLi s t ener ( "c l ick" , funct ion ( ) var captureUI = new capture.CameraCaptureUI ( ); captureUI.photoSett ings.f ormat = '+capture.CameraCaptureUI PhotoFormat.png; captureUI.cap tureF i l e Async ( capture.CameraCap tureU IMode.photo ). '+done 1 funct i on ( photo ) { i f ( photo ) { } } ) ; } ) ; } ) ; 1 1 Использовать Fi l e API из HTMLS для создания 1 1 URL объекта, с сылающегося на файл фотографии. var photoUrl = URL.createObj ectURL ( phot o ); 1 1 Показать фотографию в элементе IMG imgPhoto.src = photoUrl; document.addEventLi s tener ( "DOMContentLoaded" , ini t ); } ) ( ) ; Создаем первое приложение Магазина Windows 111 ••&1 Примечание. Удаленный из файла Def aul t. j s код служит для обработки событий жизненного цикла приложения, например активации и приостанов­
ки. Эти события мы рассмотрим в главе 9. В коде, приведенном в листинге 1.2, происходит немало интерес­
ного. Рассмотрим его по частям. Сначала я создал функцию init ( ),которая вызывается в ответ на событие ooмcontentLoaded. Это стандартное событие DOM, которое браузер генерирует, после того как закончит разбор НТМL-документа. Я поместил весь код в функцию ini t ( ),так что он не начнет вы­
полняться раньше, чем объектная модель документа (DOM) будет готова. В противном случае попытка обратиться к любому НТМL­
элементу на странице привела бы к исключению, так как элемент еще не существует. В функции ini t ( ) я первым делом вызываю метод WinJS. ur. proces sAl l ( ), который обрабатывает все присутствующие на странице элементы управления. В частности, элемент DIV с атрибу­
том data-win-control="WinJS. UI .AppBar" преобразуется в панель приложения. Далее я подготавливаю обработчик события для команды Take Pic­
tшe. В результате щелчка по соответствующей ей кнопке на панели приложения создается экземпляр класса Windows. Media. Capture. CameraCaptureUI. Это пример класса среды выполнения Windows. Метод CameraCaptureUI. captureFileAs ync ( ) показывает экран для фотографирования (рис. 1.1 2). При нажатии кнопки ОК вызыва­
ется метод done ( ) , который отображает фотографию на странице. Рис. 1. 1 2. Интерфейс работы с камерой IEll •• •I[ Глава 1. Разработка приложений Магазина Windows Метод URL. crea teObj ectURL ( ) создает URL ВLОВ-объекта (дан­
ных фотографии), который вернул метод captureFi l eAs ync ( ). Ме­
тод crea teObj ectURL ( ) входит в состав File API из HTMLS. Для показа фотографии на НМТL-странице служит следующий код: 1 1 Показать фотографию в элементе IMG imgPhoto.src = photoUrl; Вот и всё! Мы написали приложение, которое позволяет делать снимки с помощью компьютера и отображать их на НТМL-странице. Отметим, что в нашем JavaScгipt-фaйлe встречаются вызовы как стандартных функцийJаvаSсгiрt, так и методов HTMLS, библиотеки Windows для JavaScгipt и среды выполнения Windows. Это обычная ситуация в приложениях Магазина Windows. Запуск приложения Созданное приложение можно запустить, нажав зеленую кнопку Run на панели инструментов Visual Studio (рис. 1.13) или просто нажав клавишу FS. � Арр1 - IAicrosoft Vi ны1 Studi o FILE EDIT VIE\'i GI T PROJECТ BUI LD DEBUG ТЕАМ SQL Т у ... local Machine - Debug Рис. 1. 1 3. Запуск приложе ния Магазина Wi ndows Если ваш ноутбук или планшет оборудован камерой, можете де­
лать снимки. Предупреждение. Напомним, что команда Take Pi cture находится на панели приложения, а эта панель по умолчанию не видна. Чтобы показать ее, нужно либо щелкнуть правой кнопкой мыши по приложению, либо провести паль­
цем от верхнего или нижнего края экрана к центру. Из чего с остоит приложение Магаз ина Wind ows В предыдущем разделе мы видели, что в приложении Магазина Windows участвует несколько технологий - как открытых и хоро­
шо знакомых (HTMLS, .J avaScгipt и CSS 3), так и принадлежащих Micmsoft (библиотека Wi11dows для JavaScгipt и среда выполнения Из чего состоит приложение Магазина Wi ndows Windows). Хотелось бы сказать несколько слов о каждой из этих со­
ставных частей. JavaScript Вся эта книга посвящена написанию приложений Магазина Windows на языке JavaScгipt. Такие приложения можно писать также на С#, Visual Basic и даже С++. При разработке приложений Магазина Windows можно пользо­
ваться средствами, вошедшими в ECMAScгipt 5 - последнюю вер­
сию J avaScгipt. Следовательно, к вашим услугам такие новые методы объекта Апау, как indexOf ( ) и forEach ( ). Доступны также методы чтения и установки свойств и директива «use stгict». HTML5 При написании приложений Магазина Windows можно пользоваться многими новыми средствами HTMLS и сопутствующих стандартов. Перечислим наиболее важные: • Атрибуты проверки форм. Для проверки полей формы можно применять новые атрибуты, появившиеся в стандарте HTMLS. Мы будем говорить о них в главе 5 «Создание форм». • data-*. У элементов HTMLS могут быть нестандартные атри­
буты с таким префиксом. В библиотеке Win.JS атрибуты с пре­
фиксом data- * служат для декларативной привязки к данным и декларативного создания экземпляра элемента управления. • API индексированной базы данных ( I ndexedDB ). Этот API раскрывает интерфейс базы данных в браузере. Если требует­
ся сохранить в базе данных список товаров прямо из прило­
жения Магазина Windows, то IndexedDB будет очень кстати. Как пользоваться этим API, я расскажу в главе 8 «Создание источников данных». File API (файловый AP I ). HTMLS File API позволяет работать с файлами в браузере. Мы уже использовали его при создании нашего первого приложения Магазина Windows. • Холст (Canvas). Позволяет рисовать с помощью JavaScript. Введение в эту технологию будет приведено в главе 1 2 << Гра­
фика и игры». • Рабочие веб-процессы. Позволяют исполнять задачи в фоно­
вом режиме, не блокируя поток пользовательского интерфей­
са. --·· • •n Глава 1. Разработка приложений Магазина Wi ndows css з При создании приложений Магазина Windows нам доступны некото­
рые новые возможности стандарта каскадных таблиц стилей CSS 3 (и связанных с ним), в том числе: Опрос носителя. Позволяет применять различные стили в зависимости от таких характеристик устройства, как высота, ширина или состояние просмотра. Эта тема будет обсуждаться в главе 9. Сеточная верстка CSSЗ. Позволяет верстать НТМL-содержи­
мое в виде строк и столбцов, не используя НТМL-таблицы. Гибкая блочная верстка CSSЗ (FlexBox). Позволяет сохра­
нять относительное положение и размеры элементов при отоб­
ражении НТМL-содержимого на различных устройствах. Среда выполнения Windows Среда выполнения Windows (WinRT) включает библиотеку классов, которой можно пользоваться в приложениях Магазина Wiвdows. Эти классы проецируются непосредственно на J avaScгipt, то есть выглядят как встроенные объектыJаvаSсгiрt. Например, в нашем первом приложении Магазина Windows мы воспользовались классом WinRT Windows. Media. Capture. CameraCaptureUI. Благодаря его методу CameraCaptureUI. captureFil eAsync ( ) мы и смогли делать фотографии. Все классы WiвRT находятся в пространстве имен Wiвdows. Нап­
ример, чтобы создать экземпляр класса Cameracaptureur нужно на­
писать: var captureUI = new Windows.Medi a.Capture.CameraCaptureUI ( ); Примечание. Отметим, что имена классов Wi nRT длинны до безобразия. Поэтому имеет смысл сопоставлять пространствам имен псевдонимы, на­
пример: var capture = Windows.Medi a.Capture; Классы WinHT наделяют JavaScгipt функциональностью, необхо­
димой для создания приложений Wiвdows. Всеми возможностями, которые недоступны - или просты бессмысленны, - при разработке веб-сайтов. Ниже перечислены некоторые удивительные вещи, кото­
рые можно делать с помощью этих классов. Из чего состоит приложение Магазина Wi ndows ...• • " Геолокация. Класс WinRT Windows. Devi ce s. Geoloca tion. Geolocator позволяет получить текущую широту и долготу. Доступ к файлам. Классы в пространстве имен Windows. Storage позволяют читать и записывать файлы. Компас. Класс Windows. Devi ces. Sensors. Compas s позволя­
ет узнать направление на истинный север. Печать. Класс Windows.Printing.PrintManager позволяет печатать из приложения Магазина Windows. Сжатие файлов. Классы из пространства имен Windows. Storage. Compre s s i on позволяют упаковывать и распаковы­
вать файлы. Библиотека Windows для JavaScript Библиотека Windows для JavaScript (WinJS) - написанная на чис­
том JavaScгipt библиотека, разработанная Майкрософт специально для создания приложений Магазина Window. Основная цель данной книги - научить вас работать с ней. В библиотеке WinJS находятся все элементы управления WinJS, применяемые для конструирования пользовательского интерфейса приложения. Например, элемент DatePicker предназначен для вы­
бора даты. А что с jQuery? j Queгy - самая популярная в мире JаvаSсгiрt-библиотека. Поэтому возникает естественный вопрос: можно ли использовать ее в прило­
жениях Магазина Windows? Примечание. Согласно данным на сайте Bui l tWith, более 57% сайтов из 1 0000 лучших написаны с использованием j Query. По распространенности эта библиотека намного опережает всех конкурентов. См. http://treпds. Bui ltWi th.com/javascri pt. Да, можно. И сейчас я покажу, как. Самый простой способ добавить j Queгy в проект приложения Магазина Windows - воспользоваться диспетчером пакетов в Visual Studio. Выберите из меню команду Tools � Libгai·y Package Manageг � Package Manageг Console ( Сервис � Диспетчер пакетов � Консоль диспетчера пакетов) и введите на консоли команду установки пакета j Queгy (рис. 1.14). IEill ••• I L Глава 1. Разработка приложений Магазина Windows '· <";< P.!lck.!lge !>ourc� A!I � i) DtJ11ult proj!ct: д.ррl Р!-1.:>-1n5t al l · P.!lcki!lge jQueгr· SucJ: es.s.f ul l �" installed 'j QueГ)' 1.8.2', Success fu l l y added 'j Queгy 1. В. 2' to Appl. РМ> 1 Рис. 1. 1 4. Добавление j Query в проект с помощью консоли диспетчера пакетов Команда i nstall-package j Queгy создает в проекте папку Scгipts с тремя файлами: полный вариант j Queгy, минимизированный вариант j Queгy и файл iпtellisense. Последний дает Visual Studio возможность применять I ntelliSense к методам j Queгy. В листинге 1.3 приведен НТМL-код, в котором используетсяj Quегу. Листинг 1.3. Исполь з ов а ни е jQuer y в п ри лож ении М а га зи на Wi ndows <!DOCTYPE html> <html> <head> <meta charset ="ut f - 8" /> <t i t le>j QueryWindows B </t i t le> <!- - Ссылки на WinJS - - > < l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui - dark.cs s • rel ="s tylesheet • /> <script src="//Microso f t.WinJS.1.0/j s/base.j s"></script> <script src="//Microsof t.WinJS.1.0/j s/ui.j s"></s cript > <!- - Ссылка на j QueryWindows B - - > <script type="text/j avascript" src="Scripts/j query-1.7.2.j s"></script> <Styl e type="t ext/c s s"> #divMes s age { display: none; padding: l Opx; border: s ol i d lpx whi t e; background-color: #f f б aO O; </s tyle> </head> <body> <button id="btnShow">Cl i ck Here</but t on> <div i d="divMes s age"> Secret Mes s age </div> <script type ="text/j avascript"> $ ( • #btnShow") . c l ick ( funct i on ( ) Построение приложения Магазина Windows в Visual Studio $ ( "#di vMes sage") . f adeToggl e ( "s l ow") ; } ) ; </script> </body> </html > ...• • " Примечание. Код из листинга 1 .3 находится в папке j QueryWi ndowsB проек­
та Chapter1. Страница, показанная в листинге 1.3, содержит элементы BUTTON и DIV. По умолчанию содержимое DIV скрыто (благодаря стилю display: none), а после нажатия кнопки оно медленно прояв­
ляется на экране (рис. 1.1 5). Рис. 1.1 5. Использование jQuery для анимации элемента DI V Предупреждение. В приложениях Магазина Wi ndows JavaScri pt- кoд ис­
полняется в локальном контексте с дополнительными ограничениями бе­
зопасности для предотвращения атак с внедрением скриптов. В частности, запрещено записывать в свойство элемента i nne rHT ML потенциально опас­
ный НТМL-код, содержащий, к примеру, скрипты или некорректно сформи­
рованную разметку. Если используется JаvаSсri рt-библиотека, написанная без учета этих ограничений, в частности j Query, то, возможно, придется мо­
дифицировать ее для работы в приложении Магазина Wi ndows. Если вы до­
веряете содержимому, то вместо присваивания свойству i nner HTML можете воспользоваться методом Wi nJ S. Ut i l i t i es. s et i nner HTMLUn s a f e ( ). Построен ие приложения Магаз ина Win dows в Vi sual Stu dio Это книга о создании приложений Магазина Windows с помощью Visнal Stнdio. В этом разделе я хочу посвятить несколько страниц описанию тех средств Visнal Stнdio, которые важны для разработки приложений Магазина Windows. Вы узнаете, как выбрать шаблон проекта, как запускать и отлаживать такие приложения. .:r:ll •• • I G Глава 1. Разработка приложений Магазина Windows Шаблоны проектов приложений Магазина Windows Выполнив команду File � New Pгoj ect ( Файл � Создать проект) в Visual Studio, вы затем сможете выбрать в качестве отправной точки один из пяти шаблонов проекта приложений Магазина Windows. 1. Blank Арр ( Пустое приложение). Самый простой шаблон. По­
рождает только файлы default.l1tml, default.css и default.js. 2. Navigation Арр ( Приложение навигации). Используется для приложений с несколькими страницами. 3. Grid App (Приложение таблицы). Содержит две страницы. На одной отображается список элементов, разбитый на группы, на второй - подробные сведения о выбранном элементе. 4. Split Арр (Приложение с разделением). Содержит две стра­
ницы. На одной отображается список групп, на второй список элементов и подробные сведения об одном элементе. 5. Fixed Layout Арр ( Приложение с фиксированным макетом). Масштабирует элементы, сохраняя пропорции, независимо от разрешения экрана. Шаблон Blank Арр мы уже использовали при создании первого приложения Магазина Windows. Теперь давайте рассмотрим осталь­
ные шаблоны проектов. Ша бл он приложения навигации Шаблон Blank Арр хорош для простого одностраничного прило­
жения. Но если требуется поддержать несколько страниц, то следует использовать шаблон Navigatio11 Арр. Этот шаблон включает одну страницу с именем home. Чтобы по­
явились дополнительные страницы, нужно добавить страничные элементы управления в подпапку pages (рис. 1.1 6). О создании мно­
гостраничных приложений я подробно расскажу в главе 10 «Фраг­
менты страниц и навигация�>. Следующие два шаблона - Gгid Арр и Spl i t Арр - построены на основе шаблона Navigatio11 Арр. Иными словами, приложения табли­
цы и приложения с разделением - это многостраничные приложения с дополнительными страницами. Шабло н приложе ния табл ицы Шаблон Gгid Арр отображает множество элементов, разбитых на группы. Для получения подробных сведений об элементе нужно по нему щелкнуть. Построение приложения Магазина Windows в Visual Studio (;') ·0 - <t r� i3il riJi1 i;':lj 5Earch ScJutlon E).pl orrtr f(trl +;J ;J Solution 'NavigatioпApp1 · (1 pюject) • � NavigationApp 1 С» •·• Rf.ferf.nc� � ill css � ii im;,g� .О ddault.html � N11·.;igationAppl_Tempor11ryK�.pfx � package.appxma nife;t Рис. 1. 1 6. Созда ние многостраничного приложения по шаблону Navigatioп Арр Пусть, например, требуется создать каталог товаров и пусть в нем имеются различные категории товаров, скажем Beveгages (Напитки) и Fгuit ( Фрукты). Каждая категория является группой и может со­
держать несколько товаров. Приложение с шаблоном Gгid Арр отображает страницы по-разно­
му в зависимости от текущего состояния просмотра. Если приложение не прикреплено, то будет две страницы. На странице groupedi tems показаны группы и входящие в них элементы (рис. 1.1 7). JДелчок по элементу открывает страницу i temDetail с подробными сведениями об этом элементе (рис. 1.1 8). Рис. 1.1 7. Страница groupedltems в приложении с шаблоном Gri d Арр "..... Глава 1. Разработка приложений Магазина Windows Рис. 1. 1 8. Страница itemDetai l в приложении с шаблоном Grid Арр Если приложение прикреплено, то появляется дополнительная страница. Сначала выбирается группа (рис. 1.19), потом - элемент (рис. 1.20) и только потом - подробности (рис 1.2 1 ). Рис. 1. 1 9. Страница groupedltems в прикрепленном приложения с шаблоном Grid Арр Построение приложения Магазина Windows в Visual Studio U 11• • 11:11 Рис. 1 .20. Страница groupDetai l в прикрепленном приложении с шаблоном Grid Арр Рис. 1.21. Страница itemDetai l в прикрепленном приложении с шаблоном Grid Арр 1188• 1 1[ Глава 1. Разработка приложений Магазина Wi ndows Ша бл он приложения с раз дел ением Этот шаблон полезен, когда нужно показать группы элементов, например товары, разбитые на категории. Шаблон Split Арр состоит из двух страниц: i tems и spl i t. На странице i tems отображается список групп. Так, на рис. 1.22 показана страница i tems с категориями товаров. Рис. 1.22. Страница items в приложении с шаблоном Spl i t Арр Щелкнув по группе, вы перейдете к странице split. На ней отоб­
ражается список элементов, принадлежащих данной группе - товаров в выбранной категории - и предоставляется возможность получить подробные сведения о выбранном элементе (рис. 1.23). Как и Gгid Арр, шаблон Split Арр ведет себя по-разному в зави­
симости от того, находится приложение в прикрепленном состоянии или нет. Если приложение прикреплено, то пользователь будет пере­
ходить от группы к элементам внутри группы и далее - к отдельному элементу. Ша бл он приложения с фикси ров анным макетом Шаблон Fixed Layout рассчитан на весьма специфический сцена­
рий. В этом случае НТМL-страница вк лючает элемент управления ViewBox, который изменяет размер своего содержимого, так спобы оно уместилось в доступной области экрана с сохранениеJ\'1 нропорций. Построение приложения Магазина Windows в Visual Studio а11 ••111Е11 Рис. 1.23. Страница spl it в приложении с шаблоном Split Арр Представьте, что имеется фотография Теслы с разрешением 80Ох484 пикселей, и вы хотите, чтобы оно заняло всю доступную пло­
щадь экрана, но при этом не искажалось. На рис. 1.24 показано, как выглядит эта фотография на устройстве с разрешением 1 024х768. Обратите внимание, что было автоматичес­
ки произведено масштабирование под располагаемое место. Рис. 1 .24. Фот ография на устройстве с разрешением 1 024х768 пикселей Если та же самая фотография просматривается на устройстве с другим разрешением, то она будет автоматически масштабирована. ".... • Глава 1. Разработка приложений Магазина Windows Так, на рис. 1.25 показано, что произойдет при отображении этой фотографии на устройстве с разрешением 192Ох 1 080. Теперь по обе стороны от картинки находится пустое место - чтобы избежать ис­
кажений. Рис. 1.25. Фо тография на устройстве с разрешением 1 92Ох 1 080 пикселей Элемент ViewBox автоматически изменяет размеры своего дочер­
него элемента и в случае прикрепления приложения (рис. 1.26). Рис. 1.26. Уменьшенная фотография в прикрепленном приложении Построение приложения Магазина Windows в Visual Studio 111••--
У элемента ViewBox есть ряд серьезных ограничений. Он может иметь только один дочерний элемент, то есть с набором фотографий работать не будет. Кроме того, ViewBox не умеет работать с текстом, он может изменять только размер изображения или холста. Использовать элемент ViewBox (и шаблон Fixed Layout) имеет смысл главным образом в играх, когда требуется, чтобы игра выглядела одинаково на экранах компьютеров и планшетов с различным разрешением. Ведь не хотелось бы, чтобы злобная птица ( angгy Ьiгd) на одном устройстве выглядела упитанной, а на другом - тощей. Примечание. В реализации элемента ViewBox используются функции t r ansl at e и s c al e, определенные в CSSЗ. Запуск приложения Магазина Windows Visual Studio позволяет запускать приложение Магазина Windows в одном из трех режимов: Local Machine (Локальный компьютер); Simulatoг ( Эмулятор); Remote Machine ( Удаленный компьютер). В режиме Local Machine приложение Магазина Windows работает так, будто установлено на локальный компьютер. При этом использу­
ется разрешение экрана и возможности машины разработки (той, на которой исполняется Visual Studio ). В режиме Simulatoг приложение запускается в отдельном окне (рис. 1.27). Преимущество этого режима в том, что он позволяет эму­
лировать устройства разного типа. Например, перейдя из режима мыши в базовый сенсорный режим, можно эмулировать сенсорное ус­
тройство, например планшет. Можно также тестировать приложение при различном разрешении экрана. И последний режим позволяет развернуть и запустить приложе­
ние Магазина Windows на удаленном компьютере. Но предваритель­
но нужно указать этот компьютер в окне страниц свойств проекта (рис. 1.28). После того как имя удаленного компьютера задано, можно раз­
вернуть и запустить на нем приложение, выбрав соответствующую команду на панели инструментов Visual Studio. Предупреждение. Чтобы можно было развернуть и запустить приложение на удаленном компьютере, на нем должна быть установлена программа Re­
mote Tool s for Vi sual Studi o 201 2. Скачать ее можно с сайта Mi crosoft.com. llПll •••li Глава 1. Разработка приложений Магазина Wi ndows Рис. 1.27. Эмулятор, встроенный в Visual Studi o {v.t>;1"'�' k!:.�i.>�"11� • :::..«..,..,-.:;."��r:�.... r�"'""i-�11.: -::;",""' Рис. 1.28. Задание имени удаленного компьютера Отладка прилож ения Ма гази на Windows Я по натуре оптимист и верю, что написанная мной программа с пер­
вого раза выполнится без ошибок. Правда, пока что такого не случа­
лось. Я трачу кучу времени на отладку кода, который не желает де­
лать то, что я хочу. Отладка приложения Магазина Wi ndows ...•• " В этом разделе я расскажу о средствах отладки, имеющихся в Visual Stнdio. Мы обсудим, как использовать окно консоли JavaScгipt, как работать с точками останова и с обозревателем модели DOM (DOM Ехрlогег). Окно консоли JavaScript в Visual Studio Разрабатывая JavaScгipt-кoд для страниц сайтов, я пользуюсь консолью JavaScгipt в браузере для просмотра ошибок, выданных интерпретатором. Туда же - методом console. log ( ) - я вывожу свои отладочные сообщения (рис. 1.29). � flcments �� Rcsources @uetno� � Sources {J Trneline С:1 Profies � Audits J � Console ! "Cbject !'l�f.':.t!: �t.1'pt<!p" ;:iri c:!: В.93 • �-�'f':);;�) __ : oьject ф lnc.aui{ ht i"1efue!'lc.<!Er-ror: ::;r щ::luc·t� i:< not defir:ed Рис. 1 .29. Отладка с помощью консоли Ja vaScript в браузере Googl e Chrome Приложение Магазина Windows не имеет доступа к консоли JavaScгipt браузера. Вместо нее к вашим услугам консольjаvаSсгiрt в Visual Stнdio (рис 1.30). Х Cle�r О 1 .... О .. ��-����. О 2 Messag� С} SCl'<!Pтso�"i;·--u�;:;;·��le� e%ceptio!'I at li.ri!!' 14" c!:>lUr,!11 5 in па-аррх://3!!1478"6З·е92: iiх830э1ЗЫ - Ja,,,.;;S;:rip1 r untim� �ri·uг: Vo1·iis1.Jle u!'Ll�f"iп�LI j 11 �tr·l.i:t m::1d� Fili!: defaul't.j!>-, l i n�: 14 colu::'in: 5 (obji:=ct Objt=ct] < »[ > • • Рис. 1.30. Окно консоли Ja vaScri pt в Visual St udi o На этой консоли отображаются как сообщения об ошибках, так и отладочные сообщения, выводимые методом console. log ( ). Если, столкнувшись с ошибкой, вы захотите распечатать значение какой-то переменной jаvаSсгiрt, то достаточно будет ввести ее имя в нижней строке консоли (рис. 1.31 ). Примечание. Окно консоли JavaScri pt появляется только в о время работы приложения. Если вы не можете его найти, выберите из меню пункт Debug -? Wi ndows -? JavaScri pt Consol e ( Отладка -? Окна -? Консоль JavaScri pt ). IEПll ••• Глава 1. Разработка приложений Магазина Windows ,,, [!:Ьject OЬject] price < »prod1Jctl { ... } •Lгptop• 8.9S > " ' � . Рис. 1.31. Распечатка переменной Jav aScript в окне консоли Ja vaScri pt Точки останова Если разрабатываемое приложение Магазина Windows ведет себя странно, то полезно расставить точки останова и пройти по коду в пошаговом режиме. Чтобы поставить точку останова, щелкните мышью в левом внут­
реннем поле редактора кода в интересующей вас строке (рис. 1.32). Когда приложение, запущенное в режиме отладки, остановится в этой точке, вы сможете просмотреть значение любой переменной, за­
держав над ней курсор мыши. getCount: fuщ:ti;:;ri ( ) { v-'!r H111t • t'1�.�; !'�t uri1 !1'!'1'>' \'1i nJS.Pr�ise ( fur.::1·ioo ( cor,ple te, l!rror) { t h11t._ getOЬjectStoгe ( ).t hen ( t:ю:.:t i1:m ( s taгe) { 11ar reqCo unt; } j; "· i f (th at._ curs orO ptions) { ·,,..;н· cur sorOptions = th11t._cuгsorOptians: var· iпdex r store .i ndex ( cursorD ptio пs. indl!1':tlair.e): vr:or keyRa nR_e" that. cгeateKe yRang e( cu гsorOpti ons ).; f�qcoun"t" в � "f.��-�··,9,,: :. i�ge)h } !!l!i� { · ·� liil Jowu Q. " "SciFi" : } r eqco unt • : �o;;:;'pen Q. • ���i�i·t:t ; re qCoun t.onerгc _.;t upperO pt.n f1lse гe qCount.onsucct!SS • rurfC'tlt!11' lt!Vt:') t coтplete ( e'.lt. t<Jrget. resul t); Рис. 1 .32. Уста новка точки останова Для пошагового исполнения кода нажимайте кнопку Step Into (Шаг с заходом) на панели инструментов или клавишу F1 1. Примечание. Точку останова можно создать также с помощью команды Ja­
vaScri pt debugger. Работа с обозревателем модели DO M К числу моих любимых инструментов разработчика, встроенных в браузер, относится также инспектор HTML (он входит, например, в Отладка приложения Магазина Wi ndows Fi гebug). С его помощью можно просматривать HTML и СSS-код в «живом» документе. Visual Studio поддерживает аналогичный инструмент - DOM Ехрlогег (обозреватель модели). Он позволяет опрашивать свойс­
тва любого элемента HTML в работающем приложении Магазина Windows. После запуска приложения Магазина Windows в Visual Studio окно обозревателя модели DOM можно увидеть, выбрав из меню пункт Debug � Windows � DOM Ехрlогег ( Отладка � Окна � Проводник DOM). В этом окне можно щелкнуть по любому элементу и увидеть все его свойства, в том числе сведения о стилях, ассоциированных с элементом (рис. 1.33). < ! DOCTYPE html) ... <html > t> ·.C:head>�.</head> ... <body> <di v i d•'"tmplHo\'i e" styl i::""dis.pluy: мrн!;ц data· 1-lin-cont rol,.."Hi nJ S. Бi ndi ng. Ter:ip l ut E:" dZita -1,·i n · h,as:fr.o;gm�r>'t �-"" ></di v> t> <div>�.</di•:> t> <div tat-i�di::x•"-1" cl a ss•"win-li� t,1i:=1-J �tin­
s1�ipeaЫe" iФ•"lvHov i i:! s." rol�·"l i s:t bc-x" a r-ia -
11шl ti s i: l ·.:c t ut: l E• ·"f <:; l se� styl �·"po�iti o r.: relat ive;" dat з-\'lir,··.:oг,t re>l"'"Ыi n"JS.Ul. LiиVi!::N" d'1til -..-in · o�ti,,:ir;:;·"" { i t�mT стр la t!:!: 5elect: ( 'i+tmplf·!�vie') .1 �el!:!c t: ior1Node: 's i ngl e' } ">".</ di v> t> <farm id ="f1·mAdd">.�<i fo1·m> ! E:�:�:�iO:�Jff�:�:!�i����:�:�i�����!.����O���::::��:�::�.:·: ··················:::::::�:i ([шttan id �"btnEdi t">Edi t</buttoг1> <button l d" "bt riDe.l �te") Delete{ Jbutton> </body> </html:;. � fr;н:-e St'f>ES �yc-ut 4.t!ributes E�enR ""'i nh�ri t�d .о11 < html > html, body { � -rns -scrcl l -tr ar.::s1 •. ver t i cal - t o- harizc nt iJ l; � c;;r!>or: r:lefault; .o11</1tml > ::selectio г; { нi -ден·k"с r. � } �i:r,,1or-: ОгgЬ(2.55, 255, 255); .о11 <body> html, had)• { ui -darl cc:::� [;ll ··ms-s:crcll ·tгйм'l • .ver t i cal -t o- hori:.::cnt al; �cur::.or: r:lef..sнl i:; .о11 <bod}» body, h5, .нin ·type-511'1&11, нi -,jaj· k"c�� legl'!nd { j �� -
f;ll ·f-;-··· · 1е: i.,,�··· · [;/Jl<,o�;,I>'"'-
.о11 <body> body� buttan, input, tii ·-dal" k.cs::o t'l!xtar�&, .1·•i n-te-x:t<!re.a, !ie-l �ct:, option { �l etter-.spaci.ng: о.е2�т; .о11 <body> bady { ui -dc:>l" k. с:::::: [ilcolo r: 0rg'!](255, 255� 2S5); < > Рис. 1.33. Окно обозревателя модели DOM Щелкнув по элементу, ассоциированному с элементом управления WinJS, вы увидите все элементы и атрибуты HTML, составляющие этот элемент управления. Например, элемент управления Li s tView порождает множество элементов D IV. Глава 1. Разработка приложений Магазина Windows Публ икация в Магаз ине Wi ndows Наверное, основным мотивом для разработки приложения Магази­
на Windows является желание продать его - ради денег или славы. В этом разделе я расскажу о том, какие шаги следует предпринять, чтобы опубликовать свое приложение в Магазине Windows. Примечание. Распространять свое приложение можно и без публикации его в Магазине Wi ndows - с помощью так называемого перекачивания ( si de­
l oadi ng). Но для этого следует подписать свое приложение и правильно настроить групповые политики на целевых компьютерах. Дополнительные сведения о перекачивании можно найти на странице http://technet.mi crosoft. com/en-us/l i brary/hh852635.aspx. Регистрация в качестве разработчика Магазина Windows Чтобы опубликовать свое приложение в Магазине Windows, нужно предварительно зарегистрироваться в качестве разработчика. Создать учетную запись можно на панели мониторинга Магазина Windows в центре разработки. Процедура регистрации несложна. В настоящее время за право стать зарегистрированным разработчиком для Магазина Windows приходится платить 99 долларов в год (рис. 1.34), а при наличии под­
писки MSDN это бесплатно. Pri ce Your i-.;�q istratioo i;к\кies: • Vour 'Ni ndo· ws Store de-..e1op« �ccount " Ассен to thl! 'liindow� Stcre da�hbo�rd • The abllit'f to puЬl i �h арр� i11 tl1e '.'�indo,"�Store • Detailed info зbout your apps in the •titndov.1s Slore • Automatic rene�val (paid $Ubsc1iptions only) You nted to regi�ter а� а comp.1ny actount, or ha11e � regiЯn1tion cod� to continue. L��=r• rй:rt \V;пc!o;vs .5\ore cJevel oper reчistcation 99.00 USD Tobl 99.00 USD Рис. 1.34. Регистрация в качестве разработчика Магазина Wi ndows Публикация в Магазине Wi ndows Отправка приложения Зарегистрировавшись, вы получаете доступ к панели мониторинга Магазина Windows и можете отправить новое приложение на рас­
смотрение. Процедура отправки приложения состоит из восьми ша­
гов (рис. 1.35). MyTasks: Release 1 (:·;:!At>•i�.;;;p�:)· �.�;,,� "'!':t�!':« ���! cl'i'i'"Y\t.1 !"'•P::):)•.tPlr;; 4"!iJ or-tb� �;i-:1.t,.;< ,_ф:.1::! !':)�"k<=:y·.+ l!r.щ;O;;<;:a"Н''(:!i:��'>'�'il<:<r. ! � t,"1!i;e it:.� !Щ• 1:""}'fl� tr.e C";·f'1efi''<�!o;i pt-;._ ; .. """. � �i.,oп;p(;,;r� ''"''':-· �"'''·�· ''"' :;;<::;< (<r�"", ... ,� -"""1 � �!'J> >.i<>-
Рис. 1.35. Отправка приложения в Магазин Windows Один из самых важных - выбор имени для приложения. Зарезер­
вировать для своего приложения имя в Магазине Windows можно еще до окончания его разработки. Выбор имени приложения - все равно, что выбор имени домена, поэтому я рекомендую застолбить имя как можно раньше. Кроме того, нужно решить, сколько будет стоить ваше приложе­
ние. В настоящее время цена может изменяться в диапазоне от 1,49 до 999,99 долларов с шагом 50 центов. Можно также предлагать при­
ложение даром. Примечание. Для i Phone существуют приложения, за которые просят 999,99 долларов, например iVI P Bl ack i Phone. Но чтобы его купить, вы должны дока­
зать, что являетесь состоятельным лицом с «активами и ( или) доходом не менее 1 миллиона фунтов стерлингов». "...... Глава 1. Разработка приложений Магазина Windows На шестом шаге - Packages ( Пакеты), - на сервер загружается готовое приложение Магазина Wiвdows. Находясь в Visual Studio, выберите из меню пункт Pгoj ect � Stoгe � Сгеаtе Арр Package ( Про­
ект � Магазин � Создать пакет приложения) (рис. 1.36). Затем мо­
жете щелкать по ссылке Packages для загрузки пакета. Creat.e Арр Package m Create Your Package Do you want to build а package to upload to the \�Jindows Store? (!) Уе5 с• No Vi sual Stud'io will download the required iпformation for the package. to Ье uploaded to the Store. ог used locally. You mшt sign into th� Store with а Mic.ros:oft account. You с;зn f.Ifi;t.:: м1 ;1c::.:.r.Н.:r1t if yo u don't ha1,•e on� 1 Sign ln 1 1 ConceJ Рис. 1.36. Создание пакета приложе ния Сертификация приложения Майкрософт должна проверить ваше приложение перед опубликова­
нием его в Магазине Wiвdows. Иными словами, приложение должно быть сертифицировано. Частично это делается с помощью автомати­
ческих программ, а частично - с участием человека. К сертифицируемому приложению предъявляется много требова­
ний. Некоторые очевидны - например, приложение не должно содер­
жать ошибок, приводящих к немедленному аварийному завершению. Также оно не должно быть примитивной рекламой вашего бизнеса. Есть и другие, не столь очевидные, требования. Например, прило­
жение должно в полной мере поддерживать работу в прикрепленном и заполняющем состоянии просмотра. Оно также не должно переда­
вать неожиданно большие объемы данных по тарифицируемому се­
тевому соединеrrию. Примечание. Требования к сертификации приложений Магазина Wi ndows подробно изложены на странице http://msdn.mi crosoft.com/en- us/l i brary/ wi ndows/apps/hh694083.aspx. Публикация в Магазине Wi ndows ...•• " Для прохождения автоматизированных сертификационных испы­
таний перед отправкой приложения в Магазин Windows можно вос­
пользоваться комплектом сертификации приложений для Windo,vs (Windows Арр Ceгtification Кit). Самый простой способ подвергнуть приложение испытанию состоит в том, чтобы собрать пакет приложе­
ния в Visual Studio (команда Package � Stoгe � Сгеаtе Арр Package). На последнем шаге мастера создания пакетов приложений предлага­
ется запустить комплект сертификации (рис. 1.37). Create Арр Package 00 Your package{s) have been created Visual Studio created your pacbge(s) in the following focation: То va1idate:your package complies with Store requireme:nU., click �Launch Windows Арр Certificrtion Юt�, • VaПdating your pac:kagt might tak:e а few minutes or /onger depeлding оп the size; of your арр. • Оле or n10re t� req uire: thi!! арр to run in full screen. • Do not interact with the machine: until you se:e:t.ht!! t� r6ults. • The �sting versionofyour i!pp on the loc11lmachin e: w Щ Ье: rW"loved. 1� Launch \Vindows Арр C�ificiltion IGt j J==:oo Рис. 1 .37. Создание пакета приложения Примечание. Комплект сертификации приложений для Wi ndows уста­
навливается вместе с Vi sual Studi o. Его можно запустить и независимо от Vi sual Studi o - выбрав соответствующую команду на начальном экране. После запуска комплекта сертификации компьютер в течение какого-то времени будет мучить ваше приложение, а затем сфор­
мирует отчет с информацией о том, прошло оно испытания или нет (рис. 1.38). Примечание. Если вы пользуетесь сервером Team Foundati on Server, то мо­
жете даже интегрировать комплект сертификации в процесс сборки. Тогда при каждой сборке приложения будут автоматически выполняться сертифи­
кационные испытания. Если приложение удовлетворяет всем требованиям и одобрено Майкрософт, то оно появится в Магазине Windows, и вы можете на­
чать грести денежки. Всякий раз как кто-то купит ваше приложение, на счет, указанный вами на панели мониторинга Магазина Windows, будут перечисляться деньги. " ••• 1 Глава 1. Разработка приложений Магазина Windows Wi ndows Арр Cer!i fi cati on Ki t - Test ResL1 i ts �j"I O'•'ft.rnt: At:-0 1'_..tfW',,.r, Q<;�'-1"< R*'ll<'AI t�>rlti. l/J/lDl:J7:15�l,M Overal l result: PASSED Лрр rnanif1•<;1 coпipli.1-r1ce tf.'<il Рис. 1.38. Отчет об (успешной) сертификации приложения, сформированный комплектом сертификации приложений для Wi ndows Р ез юме В этой главе я хотел в общих чертах познакомить вас с приложения­
ми Магазина Windows. Мы начали с обзора принципов оформления Майкрософт. Затем мы поговорили о таких стандартных свойствах приложений Магазина Windows, как панель приложения, чудо-кноп­
ки и прикрепленное состояние просмотра. Далее я шаг за шаг ом провел ва с по пути разработки приложения Магазина Windows. Мы написали крутую программу, которая умеет делать фотографии - в стандартном веб-приложении такое было бы невозможно. Вы узнали о стандартных технологиях, лежащих в основе прило­
жений Магазина Windows: HTMLS, JavaSciipt и CSSЗ. И о том, как применяются дополнительные технологии Майкрософт: среда вы­
полнения Windows Runtime и библиотека Windows дляjavaScгipt. Я также объяснил, как воспользоваться возможностями Visual Studio для Windows 8 при разработке приложения Магазина Windows: эмулятором, отладчиком и окном 1<0нcoлиjavaScгipt. Наконец, вы теперь знаете, как зарабатывать на своем приложе­
нии, опубликовав его в Магазине Windo\vs. Вы понимаете, как заре­
гистрироваться, отправить приложение и пройти сертификацию. - -
fЯА В А 2. О сно вы Wi nJS В этой главе:
• Пространства имен, модули и классы.
• Асинхронное программирование с обещаниями.
• Поиск элементов DOM с помощью селекторов запроса.
• Выполнение вызовов Ajax с помощью функции хhг.
Цель этой главы - познакомиться со средствами, вошедшими в биб­
лиотеку WinJS. Вы будете пользоваться ими практически во всех сво­
их приложениях. Первая часть главы посвящена пространствам имен, модулям и классам. Вы узнаете о рекомендуемых способах структурирования J avaScгipt-кoдa. Затем мы рассмотрим, как пользоваться еще одной возможностью библиотеки WinJS - обещтшю.ш. Это элегантный способ асинхрон­
ного программирования нajavaScгipt. Далее мы обсудим селект оры запросов, которые помогают эффек­
тивно находить элементы модели DOM НТМL-документа (вспомни­
те о селекторахj Quегу). Наконец, вы научитесь работать с имеющейся в библиотеке WinJS функцией xhr ( ).Она позволяет выполнять Аjах-запросы, в том чис­
ле междоменные. Простра нст ва име н, модул и и класс ы Майкрософт рекомендует придерживаться определенных правил структурирования JavaScгipt-кoдa приложения Магазина Windows. " ••.. Глава 2. Основы WinJS В частности, предлагается организовывать код в виде набора про­
странств имен, модулей и классов. Если следовать этим рекомендациям, то код будет содержать мень­
ше ошибок и его будет проще сопровождать. Пространства имен Сначала я хочу рассказать о том, какие в библиотеке Wi11JS есть ме­
тоды для определения пространств имен. Но прежде надо задать себе следующие вопросы. Заt1ем вообще нужны пространства имен? Ка­
ким целям они служат? Не усложняют ли они без нужды наши при­
ложения Магазина Wi11dows? В конце концов, есть немало JаvаSсгiрt-библиотек, которые пре­
красно обходятся безо всяких пространств имен. Например, j Queгy пространства имен не поддерживает, а ведь это самая популярная в мире JаvаSсгiрt-библиотека. Если j Quегу может обходиться без про­
странств имен, то почему мы не можем? Пространства имен выполняют в языке программирования две функции. Во-первых, они предотвращают конфликты имен, то есть позволяют создавать одноименные сущности. Представим себе, что две компании - А и В - разрабатывают элемент управления для поку­
пательской корзины нa javaScгipt и хотят назвать его ShoppingCart. Заведя пространства имен CompanyAИ CompanyB, они могут решить эту задачу, назвав свой элемент соответственно CompanyA. ShoppingCart И CompanyB. ShoppingCart. Вторая функция пространств имен - организационная. Про­
странства имен позволяют сгруппировать взаимосвязанные функции, даже если они находятся в физически различных файлах. Например, я знаю, что все методы библиотеки WinJS, относящиеся к работе с классами, находятся в пространстве имен Wi11JS.Class. Благодаря пространствам имен, проще понять, какую функциональность предо­
ставляет библиотека. Если вы разрабатываете простенькое приложение наJ avaScгipt, то возиться с пространствами имен нет особых причин. Но если вы поль­
зуетесь несколькими библиотеками, написанными разными людьми, то пространства имен приобретают чрезвычайную важность. Метод Wi nJS.Namespace.def i ne( ) В библиотеке Wi11JS основным методом создания пространства имен является WinJS. Namespace. define ( ). Он позволяет объявить Пространства имен, модули и классы пространство имен (любой вложенности). Метод WinJS. Name space. define ( ) принимает следующие параметры: name - строка, содержащая имя нового пространства имен. С помощью точек можно определять вложенные пространства имен. memЬers - необязательная коллекция объектов, помещаемых в новое пространство имен. В листинге 2.1 объявлено два пространства имен: CompanyA и CompanyB. Control s. В том и в другом имеется объект ShoppingCart, обладающий методом checkout ( ). Лист инг 2.1. Создание пространств имен ( namespaces\namespace s.html) // Создать пространство имен CompanyA, содержащее объект // ShoppingCart WinJS.Namespace.de f ine ( CompanyA.ShoppingCart "CompanyA" ) ; { checkout: funct i on ( ) { return "Checking out from А" } ; //Создать пространство имен CompanyB.Control s, содержащее объект // ShoppingCart WinJS.Namespace.de f ine ( "CompanyB.Control s" { ShoppingCart: { checkout: funct i on ( ) { return "Checking out from В" 1; // Вызвать метод checkout объекта ShoppingCart из пространства //имен CompanyA consol e.l og ( CompanyA.ShoppingCart.checkout ( ) ); //Выводит '+"Checking out from А" // Вызвать метод checkout объекта ShoppingCart из пространства //имен CompanyB.Controls console.log ( CompanyB.Control s.ShoppingCart.checkout ( ) ); !/Выводит '+ "Checking out from В" Здесь в результате вызова WinJS. Namespace. define ( "CompanyA") создается пространство имен CompanyA. Затем в него добавляется объект ShoppingCart. Таким образом, операции определения про­
странства имен и добавления в него объекта разделены. Глава 2. Основы WinJ S В случае пространства имен CompanyB. Control s применен другой подход: создание пространства имен и добавление объекта произво­
дятся в одном вызове метода WinJS. Namespace. define ( ) . Обратите внимание, что CompanyB. Control s - вложенное про­
странство имен. Пространство имен верхнего уровня CompanyB со­
держит пространство имен Control s. Мы можем указать имя, содер­
жащее точки, а WinJS сама позаботится о деталях создания одного пространства имен внутри другого. После того как пространства имен определены, можно в одном и том же] аvаSсгiрt-файле использовать обе корзины, не создавая конфликта. Мы можем вызвать либо метод CompanyA. ShoppingCart. checkout ( ), либо метод CompanyB. Control s. ShoppingCart. checkout ( ). Метод Wi nJS.Namespace.define Wi thPar ent() Метод WinJS. Name space. defineWi thParent ( ) похож на метод WinJS. Name space. define ( ). Оба определяют новое пространство имен. Но defineWi thParent ( ) позволяет добавить новое пространс­
тво имен в существующее. Метод WinJS. Namespace. defineWi thParent ( ) принимает следу-
ющие параметры: parentNamespace - объект, представляющий родительское пространство имен; name - строка, содержащая имя нового пространства имен, до­
бавляемого в родительское; memЬers - необязательная коллекция объектов, помещаемых в нов ое про ст ран ство им ен. В следующем коде демонстрируется, как создать корневое про­
странство имен CompanyA и добавить в него дочернее пространство имен Control s: WinJS.Namespace.def ine ( "CompanyA" ) ; WinJS.Namespace.de f ineWi thParent ( CompanyA, "Contro l s" , { ShoppingCart: { checkout: funct ion ( ) { return "Checking out"; } ) ; 1 1 Выводит "Checking out" cons ole.l og ( CompanyA.Control s.ShoppingCart.checkout ( ) ); У метода defineWi thParent ( ) есть одно существенное преиму­
щество по сравнению с define ( ) - он строго типизирован. Иначе го-
Пространства имен, модули и классы •• • •1111 воря, для представления родительского пространства имен исполь­
зуется объект, а не строка. Если допустить опечатку в имени объекта (compnyA), то во время выполнения будет выдана ошибка. Модули В JаvаSсгiрt-библиотеке хотелось бы иметь как открытые, так и за­
крытые методы. Открытые методы предназначены для вызова со стороны пользователей библиотеки, они составляют открытый API библиотеки. Напротив, закрытые методы не предназначены для вызова извне, а используются только внутри самой библиотеки. Мы не хотим, чтобы пользователи вызывали их, потому что в будущем они могут изме­
ниться. Язык JavaScгipt не поддерживает модификаторы доступа. Не­
возможно пометить объект или метод как открытый или закрытый. Никто не запрещает вызвать внешней программе любой метод и об­
ратиться к любому объекту. Единственный механизм инкапсуляции (сокрытия) методов и объектов в JavaScгipt - функции. В этом языке функция определяет область видимости переменной. Переменная в J avaScгipt имеет либо глобальную область видимости, то есть доступна в любой точке про­
граммы, либо доступна только внутри функции. Чтобы скрыть объ­
ект или метод, следует поместить его внутрь функции. Так, в следующем коде определяется функция doSomething ( ) , а внутри нее - функция doSomethingEl s e ( ): funct i on doSomething ( ) { cons ol e.l og ("doSomething"); funct i on doSomethingEl s e ( ) { consol e.l og ("doSomethingEl s e • ); doSomething ( ); // Выводит • doSomething• doSomethingEl se ( ); //Возбуждает исключение ReferenceError Функцию doSomethingEl s e ( ) можно вызывать только внутри функции doSomething ( ), то есть doSomethingEls e ( ) инкапсулиро­
вана внутри doSomething ( ) . В библиотеке WinJS инкапсуляция функций используется для сокрытия внутренних методов. Все методы WinJS определе­
ны внутри самоисполняемых анонимных функций. По умолча-
Глава 2. Основы WinJS нию скрыто всё. Чтобы сделать метод открытым, его нужно явно поместить в пространство имен, определенное в глобальной области видимости. Допустим, я хочу написать небольшую библиотеку служебных ме­
тодов. Мне нужен метод для вычисления налога с продаж и метод для вычисления ожидаемой даты отгрузки товара. Библиотека, показан­
ная в листинге 2.2, инкапсулирует реализацию в самоисполняемой анонимной функции. Лис тинг 2.2. Инкапсуляция функций внутри модуля (modules\modules.html ) ( funct i on ( gl obal ) 1 1 Открытый метод вычисления налога funct i on calculat eTax ( price ) return calculateFederalTax ( pric e ) + calculateStateTax ( pri c e); 1 1 Закрытый метод вычисления налога штата funct ion calculateStateTax ( price ) { return price * 0.0 8; 1 1 Закрытый метод вычисления федерального налога funct i on calculat eFederalTax ( pri ce ) { return price * 0.0 2; 1 1 Открытый метод, возвращающий ожидаемую дату отгрузки funct i on calculateShipDate ( currentDat e ) currentDate.setDate ( currentDate.getDate ( ) + 4 ); return currentDate; 1 1 Экспорт открытых методов WinJS.Namespace.de f ine ("CompanyA.Ut i l i t i es", { } ) ; calcul ateTax: calculateтax, calculateShipDat e: calculateShipDate } ) ( thi s ); 1 1 Вывести ожидаемую дату отгрузки var shipDat e = CompanyA.Ut i l i t i es.calculateShipDat e ( new Date ( ) 1; consol e.log ( shipDate ); 1 1 Вывести price + tax Пространства имен, модули и классы var price = 1 2.3 3; W'!� •. ··--=­
I U! .. � var tax = CompanyA.Ut i l i t i e s.calculat eTax ( price ); cons ole.l og ( price + tax); Здесь внутри самоисполняемой анонимной функции определены четыре функции: calculateTax ( ), calculateStateTax ( ), calcu­
lateFederalTax ( ) и calculateShipDate ( ).А в следующем предло­
жении раскрываются только две из них: 1 1 Экспорт открытых методов WinJS.Namespace.de f ine ("CompanyA.Ut i l i t ies", { ) ; calculat eTax: calculateTax, calculat eShipDat e: calculateShipDate Поскольку функции calculateTax ( ) и calculateShipDate ( ) помещены в пространство имен CompanyA. Uti l i ti es, их можно вы­
зывать вне самоисполняемой функции. Это методы, составляющие открытый API библиотеки. С другой стороны, методы calculateStateTax ( ) и calculate­
Federal тах ( ) навеки скрыты в черном ящике самоисполняемой функции. Они инкапсулированы, поэтому вызвать их вне этой функ­
ции невозможно. Это внутренние методы библиотеки. Классы В отличие от других популярных языков, например С# или Java, в JavaScгipt нет встроенной поддержки классов. В JavaScгipt тип (класс) и экземпляр типа (объект) не различаются. Всё в JavaScгipt -
объекты. В библиотеку WinJS включены расширения JаvаSсгiрt для созда­
ния классов. Эти методы активно используются и внутри самой биб­
лиотеки. Например, с их помощью созданы все элементы управления WinJS. В этом разделе мы рассмотрим, как можно определять новые классы, пользуясь предоставляемыми WinJS средствами. Мет од Wi nJ S.C lass.def ine( ) Новые JavaScгipt-клaccы создаются методом WinJS. Clas s. define ( ) , который принимает три аргумента. constructor - функция-конструктор, применяемая для инициализации нового объекта. Если передать null, то будет создан пустой конструктор. Глава 2. Основы WinJS instanceMemЬers - коллекция свойств и методов экземп­
ляра. staticМemЬers - коллекция статических свойств и методов. В листинге 2.3 демонстрируется создание класса Robot и последу­
ющее создание объекта этого класса - робота Roomba. Ли ст инг 2.3. Создание класса (c/ass es\c/asses.html ) var Robot = WinJS.Class.de f ine ( funct i on ( name, price ) { this .name = name; ) ; } ' { this .pri ce = price; _name: undef ined, _price: О, price: { set: funct i on ( value ) { i f ( value < 0 ) { throw new Еrrоr ("Недопустимая цена!"); thi s ._price } ' value; get: funct i on ( ) { return thi s ._price; } ' makeNoi s e: funct ion ( ) { return 11 Burp 1 Wow!, oops!"; //Создать робот var roomba = new Robot ( "Roomba" , 2 0 0.3 3 ); cons ol e.log ( roomba.pri ce ); //Выводит "2 0 0.3 3" consol e.log ( roomba.makeNoi s e ( ) ); //Выводит "Burp, Wow!, oops!" // Задать недопустимую цену roomba.pri ce = - 8 8; // Возбуждает исключение "Inval i d price!" Класс Robot определен с помощью метода WinJS. Clas s. define ( ). Первым аргументом ему передана функция-конструктор класса. Конструктор инициализирует свойства name и price класса. Следующим аргументом передана коллекция членов экземпляра. Содержащиеся в ней элементы используются для определения полей name и _price, а также свойства price. Кроме того, коллекция со­
держит определение метода makeNoi s e ( ). Пространства имен, модули и классы ...• • " Примечание. По соглашению, имена закрытых членов класса - полей, свойств и методов - начинаются знаком подчеркивания. Так, в листинге 2.3 закрытое поле, стоящее за свойством pr i ce, называется _p r i c e, а не pr i ce. Обратите внимание, что у свойства price имеются методы чте­
ния и установки. Если попытаться присвоить цене недопустимое значение, то метод установки возбудит исключение, как показано на рис. 2.1. Х Cl•or ${R!PEOl i: � lJr,hмdJ �d елсерНоn at J.i r,� 11., c-o1i;�r; 21 3x80�Hi.l39e � J a1.1�Sc�ipt rщ1time error: I n\•al i d pr i. ce! 200.33 Bur p, t.:ov1 ! .1 ощ::15 ! < »1 > Рис. 2. 1. Исключение l nval i d price! Метод Wi nJS.Cl ass.de rive( ) Метод WinJS. Clas s. derive ( ) позволяет воспользоваться прото­
типическим наследованием, чтобы произвести один класс от другого. Метод принимает четыре аргумента. baseClass - базовый класс. constructor - функция-конструктор, применяемая для ини­
циализации экземпляра нового класса. instanceMemЬers - свойства и методы экземпляра нового класса. staticMemЬers - статические свойства и методы нового клас­
са. В листинге 2.4 определены три класса: Robot, Roomba и AIBO. Класс Robot является базовым, а классы Roomba и AIBO - производными от него. Ли сти нг 2.4. Определение производных классов ( derive\derive.html ) var Robot = WinJS.Cl as s.de f ine ( func t i on ( ) { this.type = "Robot" } ' IDl•• l l J ) ; sayHe l l o: funct i on ( ) { return "Му name i s " + thi s.name + " and I am а " + this.type; var Roomba = WinJS.Cl ass.derive ( Robot, ) ; funct i on ( name ) { this.name name; thi s.type = "Roomba" var AIBO = WinJS.Cl ass.derive ( Robot, ) ; funct i on ( name ) { this.name name; thi s.type = "AIBO" 1 1 Создать Roomba var myRoomba = new Roomba ( "rover" ) ; cons ol e.l og ( myRoomba.sayHel lo ( ) ); 1 1 Создать AIBO var myAIBO = new AIBO ( "spot" ) ; cons ol e.l og ( myAIBO.sayHel l o ( ) ); Глава 2. Основы WinJS При исполнении этого кода на консоли JаvаSсгiрt в Visual Studio печатаются следующие сообщения (рис. 2.2). JзvгiScript Con�ol� х c1ear i O оЕ;;�;;, ,�o����;�;���I о з мещ•• » Му n.a:'lle is: rover аг.d I am а Raooba Ну na11e is spot and I ст а А180 Рис. 2.2. Производные роботы В этом примере конструктор класса Robot вообще не вызывает­
ся. Вместо него вызываются конструкторы классов RоошЬа и AIBO. Но оба эти класса наследуют метод sayHello ( ) от базового класса Robot. Пространства имен, модули и классы l l l • •IDI Метод Wi nJS.Cl ass.mix() Вместо метода WinJS. Clas s. deri ve ( ) можно использовать ме­
тод WinJS. Clas s. mix ( ), который позволяет создавать примеси. Ме­
ханизм примесей служит для объединения методов нескольких объ­
ектов J аvаSсгiрt в одном. За кулисами метод WinJS. Clas s. derive ( ), рассмотренный в предыдущем разделе, пользуется прототипическим наследованием, которое работает не очень быстро. На проход по цепочке прототипов уходит процессорное время. Поэтому рекомендуется всюду, где возможно, использовать вместо прототипического наследования примеси. В этом случае все методы и свойства объединяются в одном объекте, так что длинная цепочка прототипов отсутствует. Метод WinJS. Class .mix ( ) принимает следующие параметры. constructor функция-конструктор, применяемая для инициализации нового класса. mixin - массив параметров, содержащий примешиваемые методы. В листинге 2.5 показано, как с помощью WinJS. Clas s. mix ( ) ими­
тируется одиночное наследование. Листинг 2.5. Создание примеси ( mixins\mixins.html) var Robot = { } ; makeNoi s e: funct i on ( ) ( return "Ьеер" ; var Roomba = WinJS.Cl as s.mix ( func t i on ( name ) { this .name = name; } ' Robot ) ; var myRoomba = new Roomba ("rover"); consol e.l og ( myRoomba.makeNo i s e ( ) ); 1 1 Выводит "Ьеер" Здесь класс Roomba содержит все методы класса Robot. Одно из преимуществ примесей состоит в том, что с их помощью можно поддержать множественное наследование. Примешивать можно методы и свойства стольких классов, сколько необходимо. Так, в листинге 2.6 показаио, как построить класс Roomba, содержащий методы классов Robot, Pгoduct и Vacuum. Е1• • 1 11 Глава 2. Основы WinJS Листинг 2.6. Объединение нескольких объектов (mixinMultiple\mixinMultiple.html) 11 use strict"; var Robot = { } ; makeNoi s e: funct ion ( ) { return и ьеер"; var Product = { price: { set: funct i on ( value ) i f ( value < 0 ) { throw new Error ("Inval i d price!"); thi s._price = value; } ' get: funct i on ( ) } ' return thi s._price; } sayName: func t i on ( ) { return thi s.name; var Vacuum = { vacuum: funct i on ( ) { return "bz z z z z z" var Roomba = WinJS.Class.mix ( funct i on ( name ) { thi s.name = name; } ' Robot, Product, Vacuum ) ; Выводит Выводит "Ьеер11 11 rover" var myRoomba = new Roomba ( "rover" ) ; consol e.log ( myRoomba.makeNoi s e ( ) ); // consol e.log ( myRoomba.sayName ( ) ); // cons ol e.l og ( myRoomba.vacuum ( ) ); myRoomba.pri ce = - 8 8 1 1 Выводит "bz z z z z" // Возбуждает исключение Отметим, что примесь может содержать как методы, так и свойс­
тва. Более того, для свойства примеси можно определить методы чте­
ния и установки. Так, у свойства price в примеси Product имеется метод установки, в I<отором производится проверка переданного зна­
чения. При исполнении этого кода в окне консоли JavaScгipt в Visнal StL1dio печатаются сообщения, наказанные на рис. 2.3. Асинхронное программирование с обещаниями 111••1D .� . .?.Y:.�.��!.��.�.J О 3 Messэges sп�:п::·1sв2:1.: 1Jrmзrю1r.G e.:.::r.�pr.i.on rxt: l _i r; f в# .::-r.:l umri 11 :t.n GxiHHJ a139e - Jil'Y55cri;;t п.ir:timf" errt:r: I �:;a litl pr;t.ce: Fi l!': lf>i:<:ir,:·1;..1l:i p l �.j s � li r:e: i3 ce<Ji.=-�Гi: 17 b�t!p < Рис. 2.3. Звуки, издаваемые роботом Примечание. В библиотеку Wi nJS уже включено несколько примесей, кото­
рые вы можете использовать в своем коде, в том числе Wi nJ S. Ut i l i t i es. eve nt Mi xi n, Wi nJ S. UI. DOMEv entMi xi n и Wi nJ S. Bi n di ng. dynami cObser ve r s a bl eM i xi n. Ас инхронное программ ирование с обещания ми Иногда код выполняется мгновенно, а иногда требует заметного вре­
мени, а то и не завершается вовсе. Например, чтение значения ло­
кальной переменной - мгновенная операция, а получение данных с удаленного сайта при помощи Аj ах-запроса происходит куда дольше и может вообще не завершиться. Если для выполнения операции требуется длительное время, то код следует писать так, чтобы она производилась асинхронно. Вместо того чтобы ждать завершения операции, вы должны ее начать и за­
няться чем-то другим, пока не придет сигнал о завершении. Приведу аналогию: отделы обслуживания клиентов в некоторых компаниях предлагают вам оставаться на линии - и при этом слушать какую-нибудь дурацкую музыку - пока не освободится оператор. Это синхронное программирование - и бесполезная трата вашего време­
ни. Более современные службы позволяют клиенту ввести номер те­
лефона, по которому оператор перезвонит, когда освободится. Это го­
раздо гуманнее по отношению к вам, потому что в ожидании звонка оператора вы можете заниматься полезными вещами. Существует несколько паттернов асинхронного программирова­
ния. В JavaScгipt самым популярным является обратный вызов. Об­
ращаясь к функции, которая может выполняться долго, вы передаете ей функцию обратного вызова. Глава 2. Основы WinJS Обещания На сайте CommoвJS обещание определяется следующим образом (http:j /wiki.commoвj s.oгg/wiki/Pгomises ): Обещание - эт о чет ко определенный итп ерфейс взаи.модействия с обьеюпо.м, предста вляющи.м результат действия, кот орое вы­
полняется асинхронно и в произволыю указанный .мо.мент вре.ме­
ни .может оказат ься завершеины.м или незавершенным. Благодаря наличию ста ндартн ого интерфейса различные ко.мпонетп ы .могут возвращат ь обещания для асиихронных действия, а потребит ель -
использоват ь обещания предсказуе.мым образом. Обещание определяет стандартный паттерн задания обратных вызовов. В библиотеке WiвJS при создании обещания задаются три обратных вызова: для успешного завершения, для завершения с ошибкой и для информирования о ходе выполнения (последние два необязательны). Обещания используются в библиотеке Wi11JS сплошь и рядом -
для анимации, в элементах управления, для связывания и т. д. Например, метод xhr ( ) из базовой части библиотеки WiвJS возвра­
щает обещание, обертывающее стандартный объект XmlHttpRequest. В листинге 2.7 показано, как с помощью метода xhr ( ) выполнить Аj ах-запрос, который читает новостную ленту моего блога по адресу Stepheв Waltheг.com: Листинг 2.7. Создание обещани>� (promises\promises.html ) var opt i ons = { } ; url: "ht tp://s tephenwal ther.com/Ьlog/feed" responseType: "document" WinJS.xhr ( opti ons ) .done ( funct i on ( xmlHt tpReques t ) cons ol e.log ( "succes s" ); 1 1 Показать заголовок первой з аписи в бпоге var f irstTi t l e = xmlHttpReques t.respons e.querySelector ,_.( "i tem>t i t l e" ) ; cons ol e.l og ( f irstTi t l e.t extCont ent ); } ' funct i on ( xmlHt tpReques t ) cons ole.log ("fai l"); } ' funct i on ( xmlHttpReques t ) Асинхронное программирование с обещаниями consol e.l og ("progres s"); Метод WinJS. xhr ( ) возвращает обещание. В классе Promise име­
ется метод done ( ), который принимает три функции обратного вы­
зова: для успешного завершения, для завершения с ошибкой и для информирования о ходе выполнения: Promi s e.done ( completeCal lback, errorCal lback, progres sCal lback) В листинге 2.7 методу done ( ) передано три анонимные функции. Если не произойдет сетевой ошибки, то функция errorCallback не будет вызвана. Функция progre s sCallback вызывается пери­
одически по ходу выполнения Аj ах-запроса. И наконец, функция completeCallback вызывается по завершении запроса. В данном случае она выводит заголовок первой возвращенной записи в благе (рис. 2.4). » prozгe:ss progr�ss prog:ress s-ucce!>s :-i�tro \>!"lkthrough: Cгeatiг.g ll T11sk List with i!i listView аnз Ind�xe{!DB Рис. 2.4. Использование обещания v Сравнение методов then() и done() В предыдущем разделе мы задавали функции обратного вызова с по­
мощью метода done ( ) объекта Pгomise. Но у этого объекта есть еще один похожий метод: then ( ) . Метод then ( ) , как и done ( ) , позволяет настроить все три функ­
ции обратного вызова. Но тогда зачем же он нужен? Между методами then ( ) и done ( ) есть три существенных различия. В отличие от done ( ) , метод then ( ) поддерживает сцепление. Если требуется связать в цепочку несколько обещаний, то без then ( ) не обойтись. Например, в листинге 2.8 показано, как первый вызов Aj ax получает URL последней записи в моем благе, а затем (then) вызывает Aj ax еще раз, чтобы скачать содержимое этой записи. Второй вызов Aj ax не начнется, пока первый не завершится. &1•• 1 11 Глава 2. Основы Wi nJS Листинг 2.8. Сцепление обещаний ( promiseChain\promiseChain.html) var opt i ons = { } ; url: "http://s tephenwal ther.com/Ьlog/f eed" , responseТype: • document • WinJS.xhr ( opt i ons ) .then ( funct ion ( xmlHt tpReques t ) //Получить ссылку на первую запись в блоге var firstLink = xmlHttpRequest.response.querySelector ("item>l ink"); 1 1 Второй Аj ах-запрос ( возвращает обещание ) return WinJS.xhr ( { url: f i rs tLink.t extContent, responseТype: "document" } ) ; ) .done ( ) ; funct i on ( xmlHttpReques t ) { //Получить текст записи в блоге var postBody = xmlHttpRequest.response.querySelector ("div.entry"); 1 1 Вывести первые 2 0 0 символов текста записи consol e.l og ( postBody.textContent.trim ( ) .substr ( O, 2 0 0 ) ); Обратите внимание, что переданная методу then ( ) функция, вы­
зываемая при успешном завершении запроса, возвращает второе обе­
щание - результат вызова WinJS. xhr ( ) . Если второе обещание завершится успешно, то будет вызвана фун­
кция completeCal lback, переданная методу done ( ) . Она напечатает первые 200 символов содержимого записи в блоге. Сцеплять можно произвольное количество обещаний - достаточ­
но многократно вызывать метод then ( ) : then ( ) . then ( ) . then ( ) ... Последним в такой цепочке должен быть вызван метод done ( ) . Второе различие между then ( ) и done ( ) касается обработки оши­
бок. Вторым параметром методов then ( ) и done ( ) , является функ­
ция, вызываемая в случае ошибки. Ее рекомендуется всегда задавать. Но если этого не сделать, то метод done ( ) возбудит исключение, а метод then ( ) - нет. Вместо этого then ( ) вернет обещание, находя­
щееся в состоянии ошибки. Например, в листинге 2.9 выполняется Аj ах-запрос к несуществу­
ющему URL-aдpecy. Запрос, отправленный методом then ( ) , ае воз­
буждает исключение - сообщение об ошибке выводится на консоль JavaScгipt, но исполнение програ1V1мы продолжается. Асинхронное программирование с обещаниями Ли сти нг 2.9. Сравнение методов done ( ) и then ( ) ( promiseErrors\promiseErrors.html) var opt i ons = { url: "http://BadURL" } ; WinJS.xhr ( opti ons ) .then ( ); //НЕ возбуждает исключение WinJS.xhr ( opti ons ) .done ( ); //Воз буждает исключение Если вы не хотите, чтобы программа молча «глотала» ошибку, как в показанном случае применения метода then ( ) , то пользуйтесь ме­
тодом done ( ). При сцеплении обещаний всегда ставьте в конце це­
почки вызов done ( ) . Создание обещаний Обещание можно создать и самостоятельно - в виде экземпляра клас­
са Pгomise. Конструктор класса Pгomise принимает функцию с тремя параметрами: функции, вызываемые в случае успешного завершения, ошибки и для информирования о ходе выполнения запроса. Так, в листинге 2.1 0 метод wai t l O S econds ( ) возвращает обеща­
ние. Функция progres s вызывается один раз в секунду, а функция complete вызывается только по прошествии 1 0 секунд. Ли ст инг 2.1 О. Создание обещания (promiseCreate\promiseCreate.html ) funct i on wai t l O Seconds ( ) { return new WinJS.Promise ( funct ion ( complete, error, progress ) var seconds = О; var interval i d = window.s et int erval ( funct i on ( ) { seconds ++ i progres s ( s econds ); i f ( seconds > 9 ) { window.cl earint erval ( int erval i d); complete ( ); } ' 1 0 0 0 ); } ) ; wai t l O Seconds ( ) .done ( ) ; funct i on ( ) { cons ol e. log ( "c ompl ete• ) } , func t i on ( ) { cons ol e. log ( • error• ) ) , func t i on ( seconds ) { cons ol e. log ( • progres s: • + seconds ) } Глава 2. Основы WinJS Вся работа производится в конструкторе обещания. Метод window. seti nterval ( ) организует вызов переданной ему функции каждую секунду. Таким образом, один раз в секунду вызывается функ­
ция progre s s ( ) и, если уже прошло 1 0 секунд, то вызывается функ­
ция complete ( ),а затем метод clearinterval ( ). Выполнив этот код, вы увидите на консоли JavaScгipt в Visual Studio картину, изображенную на рис. 2.5. ) ) pгogress. :6 progres s:7 progr eи:S progress :9 progress: 10 complete Рис. 2.5. Строки, напечатанные объектом-обещанием Создание обещания-таймаута В предыдущем разделе мы создали собственное обещание, в котором использовали метод window. setinterval ( ) для вызова функции complete по истечении 1 0 секунд. На самом деле писать для этого свой код было необязательно, так как в классе Pгomise уже имеется статический метод, который возвращает обещание по прошествии заданного промежутка времени. В листинге 2.1 1 показано, как используется метод timeout ( ). Он возвращает обещание, которое вызывает функцию complete через указанное количество миллисекунд. Лис ти нг 2.1 1. Обещание-таймаут (promiseTimeout\promiseTimeout.html ) WinJS.Promi se.t imeout ( З O O O ) .then ( ) ; funct ion ( ) { cons ol e.log ( "compl et e" }, funct i on ( ) { consol e. l og ( "error" ) } , funct i on ( ) { consol e.l og ( "progre s s" ) Здесь обещание завершается через 3 секунды (3000 миллисекунд). Объект Pгomise, возвращенный методом timeout ( ),не поддерживает событий о ходе выполнения. Поэтому на консоль вывощпся только сообщение <1coшplete� - через 1 О секунд. Асинхронное программирование с обещаниями ••••lfD Отмена обещания Некоторые (но не все) обещания поддерживают отмену. В случае от­
мены выполняется функция обратного вызова для ошибки. Например, в листинге 2.1 2 вызывается метод w inJS. xhr ( ) для отправки Аjах-запроса. Однако сразу после отправки запрос отменя­
ется. Ли сти нг 2.1 2. Отмена обещания ( promiseCa ncel\promiseCancel.html ) 1 1 Задаем параметры Аj ах- з апроса var opt i ons = { url: "http://StephenWalther.com" } ; //Отправляем Аj ах- запрос var reques t = WinJS.xhr ( opti ons ) .then ( funct i on ( xmlHt tpReques t ) { consol e.l og ( "succes s" ); ) ; } , funct ion ( xmlHt tpReques t ) { consol e.log ( "fai l: " + xmlHttpReques t.mes s age ); } , funct i on ( xmlHt tpReques t ) { cons ol e.log ( "progres s" ); //Отменяем Аj ах- запрос reques t.cancel ( ); При выполнении этого кода на консоли jаvаSсгiрt печатается со­
общение <1fail: Canceled» (рис. 2.6). Отмена поддерживается только при использовании метода then ( ) , но не done ( ) . х (!ear ro··o·"E;;�;� �а··\.�;��-���� -fai l: Ci!!rн::e-l ed » Рис. 2.6. Отмена обещания Композиция обещаний Можно составлять новое обещание из других, то есть производить ком­
позицию обещаний. Для этого в классе Pгomise есть два статических метода: j oin ( ) и any ( ) . Обещание, возвращенное методом j oin ( ), завершается, когда завершены все обещания, переданные в качестве Глава 2. Основы WinJS аргументов. Обещание, возвращенное методом any ( ), завершается, когда завершено хотя бы одно обещание-аргумент. В коде ниже иллюстрируется использование метода j oin ( ). Но­
вое обещание, составленное из двух обещаний-таймаутов, не завер­
шается, пока не истекут оба таймаута: WinJS.Promi s e.j oin ( [ WinJS.Promi se.t imeout ( l O O O ), WinJS.Promi s e.t imeout ( S O O O ) ] ) . done ( funct i on ( ) { cons ol e. log ( "j oin complete") ; } ) ; Сообщение «complete» будет выведено на консоль JavaScгipt только после того, как завершатся оба обещания, переданные методу j oin ( ),то есть через 5 секунд (5000 миллисекунд). Метод any ( ) завершается, когда завершается любое из передан­
ных ему обещаний: WinJS.Promi s e.any ( [ WinJS.Promi se.t imeout ( l O O O ), WinJS.Promi s e.t imeout ( 5 0 0 0 } ] ) . done ( func t i on ( ) { cons ol e. log ( "any compl ete"); } ) ; Сообщение «any complete» печатается на консолиjаvаSсгiрt через 1 секунду ( 1 ООО мс), то есть сразу после завершения первого обеща­
ния, не дожидаясь завершения второго. Отб ор э ле ментов DOM с помощью се ле кторов з апроса При построении приложения Магазина Windows с помощью JavaScгipt необходим простой способ поиска элементов в НТМL-до­
кументе. Например, иногда нужно найти все теги INPUT с опреде­
ленным СSS-классом. Или один и только один элемент с идентифи­
катором favoriteColor. Стандартный способ поиска элементов в НТМL-документе - ис­
пользование селекторов. С селекторами знакомы все, кто когда-ни­
будь писал каскадные таблицы стилей, поскольку именно таким спо­
собом выделяются элементы, к которым нужно применить правило форматирования. Например, следующее правило CSS изменяет цвет фона всех эле­
ментов INPUT с классом required на красный: input.required { background-color : red } Часть «input. required» - это селектор, отбирающий все элемен­
ты INPUT с классом requi red. Отбор элементов DOM с помощью селекторов запроса ...•• " Стандарт W3C, описывающий селекторы (строго говоря, всего лишь рекомендация), называется «Selectoгs Level 3» ( Селекторы: уро­
вень 3) и находится по адресу http://www.w3.oгg/TR/css3-selectoгsj. Селекторы полезны не только для применения форматирования к определенным элементам документа, но также для наделения эле­
ментов поведением. Например, можно с помощью селектора найти конкретный элемент BUTTON и задать для него обработчик щелчка мышью, который будет что-то делать при нажатии на кнопку. Библиотека j Queгy известна великолепной поддержкой селек­
торов. Она позволяет найти в документе элементы, отвечающие се­
лектору, и каким-то образом модифицировать их. Библиотека WinJS позволяет выполнять аналогичные запросы. Примечание. Стандарт селекторов WЗС поддерживается всеми современ­
ными браузерами, в том числе Googl e Chrome, Appl e Safari и Mozi l l a Fi refox. Выполнение запросов с помощью метода WinJS. Utilities. query() В библиотеке WinJS для выполнения запросов по селектору служит метод WinJS. Uti l i ties. query ( ). Следующий 1-IТМL-документ содержит элементы BUTTON и DIV. Ли сти нг 2. 1 3. Документ, содержащий секретное сообщение ( selectorsQuery\selectorsQuery. html ) <!DOCTYPE html> <html > <head> <meta charset ="ut f - 8"> <t i t l e>Selectors Query</t i t l e> <!- - Ссылки на WinJS - - > <l ink hre f ="//Microsof t.WinJS.1.0/c s s/ui -dark.cs s" '+re l ="s tyl esheet" /> <s cript src="//Micros of t.WinJS.1.0/j s/base.j s"></s cript> <s cript src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 2 - - > <l ink hre f ="/c s s/default.cs s • rel ="styleshee t"> <script type="text/j avascript" src="s electorsQuery.j s"></script> </head> <body> <buttoп>Cl ick Me!</but toп> <div s tyle="di splay: попе"> <hl>Secret Mes s age</hl > </div> </body> </html> Глава 2. Основы WinJS Этот документ содержит ссылку на JavaScгipt-фaйл selectoгs­
Queгy.js, приведенный в листинге 2.1 4. Листинг 2.1 4. Использование селектора (selectorsQuery\sel ectorsQuery.js) ( funct i oп ( ) { 11 use stri ct"; fuпct i oп iпi t ial i z e ( ) WiпJS.Ut i l i t ies.query ("buttoп") .l i s teп ("c l ick", fuпct i oп ( ) { WiпJS. Ut i l i t i es. query ( "div"). c learStyl e ( "di splay"); } ) ; }; documeпt.addEveпtLi steпer ("DOMCoпteпtLoaded", iпi t ial i z e ); } ) ( ); В этом коде используется метод WinJS. Uti l i ties. query ( ), который отбирает все встречающиеся на странице элементы BUT­
TON. К каждому такому элементу с помощью метода l i s ten ( ) присоединяется обработчик события щелчка мышью. При нажатии кнопки BUTTON на экране появляется секретное сообщение, находящееся внутри скрытого элемента DIV. Для удаления атрибута s tyle="display: none" из DIV применяется метод clearStyle ( ) (рис. 2.7). Рис. 2. 7. Показ секретного сообщения Отбор элементов DOM с помощью селекторов запроса ••• •• 1&11 Пре дупрежд ение. Метод Wi nJ S. Ut i l i t i es. qu er y ( ) можно вызывать только после того, как документ будет полностью загружен. В листинге 2.1 4 обращение к нему находится внутри функции i n i t i a l i z e ( ),которая вызы­
вается из обработчика события DOMCont ent Load ed. За кулисами метод WinJS. Uti l i ties. query ( ) обращается к стан­
дартному методу querySelectorAll ( ). Это означает, что разрешает­
ся использовать любые селекторы, совместимые с этим методом. Ме­
тод querySelectorAll ( ) определен в стандарте WЗС Selectoгs API Level 1 (API селекторов: уровень 1 ), который находится по адресу 11ttp://www.wЗ.oгg/TR/selectot's-api/. В отличие от querySelectorAll ( ), метод WinJS. Uti li ti es. query ( ) возвращает объект класса QueryCollecti on. О методах это­
го класса речь пойдет ниже. Отбор одного элемента методом WinJS. Utilities.id() Что бы отобрать один элемент документа, а не все множество подходя­
щих элементов, можно воспользоваться методом WinJS. Uti l i ties. i d ( ).Так, следующий код изменяет цвет фона указанного элемента на красный: WinJS.Ut i l it i es.id ("mes s age") .setStyle ("background-col or•, '-+11 redн ); Это предложение отбирает один и только один элемент с иденти­
фикатором mes s age. Например, будет найден такой элемент DIV: cdiv i d ="mes s age">Hel l o!</div> Отметим, что при поиске одного элемента методом WinJS. Uti l ities. id ( ) знак решетки не указывается. Но если вы хотите сделать то же самое с помощью метода WinJS. Uti l i ties. query ( ), то знак решетки необходим: WinJS. Ut i l i t i es. query ( • #mes s age • ). set Style ( "background- color", '+"red"); За кулисами WinJS. Uti l i ties. id ( ) обращается к стандарт­
ному методу document. getElementByid ( ). Результатом WinJS. Uti l i ties. id ( ) является объект класса QueryCollection. Если элемент с указанным идентификатором не найден, то метод WinJS. Uti l i ties. i d ( ) возвращает не ошибку, а объект QueryCollection, не содержащий на одного элемента (length=O ). &1•••• Использование метода WinJS. Utilities. children() Глава 2. Основы Wi nJS Метод WinJS. Uti l i ties. chi ldren ( ) возвращает объект QueryCol ­
lection, содержащий всех непосредственных потомков элемента DOM. Пусть имеется элемент DIV с вложенными DIV'aми: <div id="di s cus s Cont ainer"> <div>Mes s age 1 </div> <div>Mes s age 2 </div> <div>Mes s age 3 </div> </div> Следующий код добавляет рамки ко всем дочерним элементам DIV, но не к их родителю (рис. 2.8). var di scussContainer = WinJS.Util ities.id ("discussContainer") .get ( O ); WinJS.Ut i l i t i es.chi l dren ( discussContainer ) .setStyle ("border •, "2рх dashed red"); Рис. 2.8. Отбор дочерних элементов Важно понимать, что метод WinJS. Uti l i ti es. chi l dren ( ) при­
меняется к элементу DOM, а не к объекту QueryCol lection. Обрати­
те внимание, что для получения элемента DOM с идентификатором di scussContainer вызывается метод get ( ). Работа с классом QueryCollection Методы WinJS. Uti l i ti es. query ( ) и WinJS. Utilities. id ( ) воз­
вращают экземпляр класса QueryCol l ection. Этот класс является производным от встроенного в JavaScгipt класса Array и добавляет несколько полезных методов для работы с элементами HTl\!IL: Отбор элементов DOM с помощью селекторов запроса ...•• " addClass ( name) - добавляет класс к каждому элементу коллекции QueryCollecti on; clearStyle ( name) - удаляет стиль из каждого элемента коллекции QueryCollection; control ( ctor, options ) - преобразовывает элементы кол­
лекции QueryCollection в элементы управления WinJS; forEach ( callbackFn, thisArg) - применяет операцию к каждому элементу коллекции QueryCollection; get ( index) - выбирает из коллекции QueryCollection элемент, находящийся в позиции с указанным индексом; getAttribute ( name) - возвращает значение атрибута первого элемента коллекции QueryCollection; hasClass ( name) - возвращает true, если для первого элемен­
та коллекции QueryCollection задан указанный класс; incl ude ( i tems ) включает коллекцию элементов в QueryCollection; listen ( eventType, listener, capture) - добавляет прослушиватель событий к каждому элементу коллекции QueryCollection; query ( query) - выполняет дополнительный запрос над элементами коллекции QueryCollection и возвращает новый объект QueryCollection; removeClass ( name) - удаляет класс из каждого элемента коллекции QueryCollection; removeEventListener ( eventType, listener, capture) -
удаляет прослушиватель событий из каждого элемента коллекции QueryCollection; setAttribute ( name, value) - добавляет атрибут к каждому элементу коллекции QueryCollecti on; setStyle ( name, value) - добавляет атрибут s tyle к каждому элементу коллекции QueryCollection; template ( templateElement,data,renderDonePromiseCont­
ract) - отрисовывает шаблон с указанными данными для каждого элемента коллекции QueryCol lection; toggleClass ( name) - переключает указанный класс для каждого элемента коллекции QueryCollection. Поскольку класс QueryCol lection является производным от класса Array, он содержит и все стандартные методы Array, в част­
ности indexOf ( ) и s l i ce ( ). Глава 2. Основы Wi nJS Вы полнение Аj ах-з апрос ов с помощь ю функции xhr(} Функцияхhr ( ) из библиотеки WinJSявляетсятонкойоберткойвокруг объекта браузера XMLHttpRequest. Но в отличие от XMLHttpRequest, функция xhr ( ) возвращает обещание. Эта функция используется для отправки Аjах-запросов. В листингах 2.1 5 и 2.1 6 приведен пример простого Аj ах-запро­
са. Запрашивается начальная страница сайта Майкрософт, после чего отображается список всех встречающихся в ней гиперссылок (рис. 2.9). Лис тинг 2.1 5. Пример простого Аjах-запроса (xhr\xhr.html ) <!DOCTYPE html > <html> <head> <meta charset ="ut f - 8"> <t i t le>Simpl e XHR</t i t l e> <!- - Ссылки на WinJS - - > <l ink hre f ="//Mi cros of t.WinJS.1.0/c s s/ui -dark.cs s" rel ="s t yl esheet • /> <script src="//Microso f t.WinJS.1.0/j s/base.j s"></script> <script src="//Microso f t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 2 --> <l ink href ="/c s s/def aul t.c s s • rel ="s tylesheet"> <script type="text/j avascript • src ="xhr.j s"></script> </head> <body> <hl >Here are the Micros of t Si t e Links:</hl> <ul id="ulResul t s"></Ul > </body> </html > Лис тинг 2.16. Пример простого Аjах-запроса (xhr\xhr.js) ( funct i on ( ) { "use stri ct"; funct ion init i al i z e ( ) // Создаем параметры xhr var opt i ons = { url: "http://Microsof t.com• Выполнение Аjах-запросов с помощью функции хhг() responseТype: "document" } ; 1 1 Отправляем Аj ах- запрос WinJS.xhr ( opt i ons ) .done ( funct i on ( xhr ) { var l i; var ulResul t s document.getEl ementByi d ("ulResul t s"); var l inks = xhr.respons e.querySel ectorAl l ("a"); for ( var i = О; i < l inks.l engt h; i ++ ) { } ' l i = document.creat eElement ("LI"); l i.innerText = l inks [ i ] .href; ulResul t s.appendChi l d ( l i ); func t i on ( ) { var mes s ageDialog = new Windows.UI.Popups.Mes s ageDial og � ( "Could not download page ! "); mes s ageDialog.showAsync ( ); } ) ; } ; document.addEventLi s t ener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Рис. 2.9. Запрос начальной страницы сайта Майкрософт и отображение встречающихся на ней гиперссылок Функция WinJS. xhr ( ) возвращает обещание. Методу WinJS. xhr ( ) в листинге 2.1 6 передаются две анонимные функции, вызыва­
емые соответственно в случае успешного завершения обещания и в случае ошибки. Если обещание завершается успешно, то внутри эле­
мента выводится список гиперссылок, а если с ошибкой - то предуп­
реждение. Глава 2. Основы Wi nJS Отметим, что в листинге 2.1 5 отправляется запрос к удаленному сайту Это должно бы насторожить вас, ведь обычно объект XMLHt tpRe­
ques t подпадает под действие правила исходного домена, запрещаю­
щего запрашивать содержимое из другого домена. Однако в контексте приложения Магазина Windows это ограничение не действует. Примечание. Начиная с l nternet Expl orer 1 0, подцерживается стандарт WЗС Cross-Ori gi n Resource Shari ng ( CORS - междоменное разделение ресурсов). Если удаленный сервер возвращает нужный НТТР-заголовок, то разрешает­
ся посылать ему Аjах-запросы. В контексте приложения Магазина Wi ndows можно не думать о CORS и отправлять междоменные запросы без каких-то специальных действий. Отметим, что URL-aдpec, которому отправляется Аj ах-запрос, входит в состав объекта параметров, который передается функции WinJS. xhr ( ) во втором аргументе. В этом объекте можно задать сле­
дующие параметры: type - метод НТТР, применяемый для отправки Аj ах-запроса, например: «POST», «GET», «PUT», «DELETE», «HEAD»; url - URL, на который отправляется Аjах-запрос; user - учетные данные для отправки Аj ах-запроса; password - учетные данные для отправки Аjах-запроса; headers - НТТР-заголовки, сопровождающие Аjах-запрос; data - данные, передаваемые удаленному серверу в Аjах­
запросе. Передавать можно строку, массив байтов и даже документ. Для преобразования других типов JavaScгipt в строку используйте метод JSON. s tringify ( ); responseType - тип данн ых, ожи дае м ых от сер в ера. Воз­
можные значения: «aпaybuffeг», «ЬlоЬ», «document», «ms­
stгeam», «text». customRequestini tializer - свойства базового объекта XmlHttpRequest. О параметрах responseType и customReque s ti nitial i z er мы подробнее поговорим в следующих разделах. Задание типа ответа Объект XmlHt tpRequest - и, следовательно, функция WinJS. xhr ( ) может использоваться для возврата данных разного типа, в том чис­
ле двоичных объектов, документов и текста. С помощью параметра re spons eType можно указать, в каком формате вы хотите получить данные. Выполнение Аjах-запросов с помощью функции хhг{) ...•• " По умолчанию respons eType равен «text». Например, следующий запрос вернет содержимое ХМL-ленты в виде строки: var opt i ons = { url: "http://s tephenwal ther.com/Ьlog/feed" } ; WinJS.xhr ( opt i ons ) .done ( funct ion ( xhr ) { var result = xhr.response; // xhr.respons e - строка ) ; Чаще всего мы не хотим получать ХМL-документ в виде стро­
ки, потому что тогда к нему нельзя применить такие методы, как querySelector ( ) и querySelectorAl l ( ) для выборки элементов. Хотелось бы получить ХМL-документ в виде объекта document: var opt i ons = { url: "http://s tephenwal ther.com/Ьlog/feed", responseТype: "document • } ; WinJS.xhr ( opti ons ) .done ( funct i on ( xhr ) { var result = xhr.response; // xhr.response - документ ) ; Отметим, что объект options содержит свойство responseType со значением «document». В результате метод WinJS. xhr ( ) возвращает данные в виде документа, а не строки, и, следовательно, мы можем применять к нему методы querySelector ( ) и querySelectorAll ( ). Примечание. Ради обратной совместимости у объекта Xml HttpRequest имеются также свойства r espons eText и r espons eX ML. Но следует no возможности использовать не их, а новое свойство r esp onse. Задание свойств объекта Xm/HttpRequest Функция WinJS. xhr ( ) - не что иное, как обертка вокруг встроенного в браузер объекта XmlHttpRequest. Иногда бывает необходимо ис­
пользовать возможности объекта XmlHttpRequest, не раскрываемые функцией WinJS. xhr ( ). В таких случаях можно воспользоваться параметром customRequestini ti ali z er для непосредственной на­
стройки объекта XmlHttpRequest. Пусть, например, требуется запросить и показать содержимое тек­
стового файла и при этом отображать ход выполнения Аj ах-запроса. " ••.. � Глава 2. Основы WinJS В листинге 2.1 7 приведена страница, содержащая НТМLS-элемент PROGRESS (предназначенный для отображения хода выполнения загрузки) и элемент DIV (для отображения содержимого текстового файла). Ли сти нг 2.17. Настройка объекта Xml HttpRequest (xhrCustom\xhrCust om.html) <!DOCTYPE html > <html> <head> <meta charset ="ut f - 8"> <t i t le>XHR Cus tom</t i t le> <!- - Ссылки на WinJS --> <l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui - dark.cs s • 4re l ="s tyl esheet • /> <script src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <script src="//Microso f t.WinJS.1.0/j s/ui.j s"></s cript> <!- - Ссылки на Chapter0 2 - - > <l ink hre f = •/c s s/def aul t.cs s • rel ="s tyl esheet"> <script type="text/j avascript" s rc ="xhrCustom.j s"></s cript> </head> <body> <progress id="prgResul t s • max="1 0 0"></progress> <div id="divResul ts"></div> </body> </html > В листинге 2.1 8 показано, как с помощью параметра customReques t i nitializ er присоединить обработчик события хода выполнения к объекту XmlHttpRequest, которым пользуется функ­
ция WinJS. xhr ( ). Этот обработчик обновляет элемент PROGRESS, так что мы можем следить за тем, как выполняется длительный Аj ах­
запрос (рис. 2.1 0). Ли ст инг 2.1 8. Настройка объекта Xml HttpRequest (xhrCustom\xhrCustom.js) ( funct i on ( ) { "use stri ct"; funct i on ini t i al i z e ( ) // Кэшируем ссылки на элементы DOM var prgResul t s document.getElementByi d ("prgResul t s"); var divResul t s = document.getElementByi d ("divResul t s"); // Задаем параметры xhr Резюме ...•• " var opt i ons = { url: "produc t s. txt" cust omReques t ini ti al i zer: func t i on ( xhr ) { xhr.onprogres s = func t i on ( evt ) { i f ( evt.lengthComput aЬl e ) { var percentCompl ete = ( evt.l oaded / evt.total ) * 1 0 0; prgResul t s.value = percentCompl et e; } } ; } } ; //Отправляем Аj ах- запрос WinJS.xhr ( opti ons ) .done ( funct ion ( xhr ) { divResult s.innerHTML } ) ; xhr.response; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ) ; Отметим, что у объекта options имеется свойство customReques ­
t initiali z er, которое представляет функцию инициализации объ­
екта XmlHttpRequest. Эта функция добавляет обработчик события onprogres s, в котором с помощью НТМL5-элемента PROGRESS отображается, какая часть запроса уже выполнена. --
F1sh Steak M1lk Coke Apples Рис. 2. 1 О. Показ хода выполнения Аjах-запроса Рез юме Целью этой главы было познакомить вас с возможностями ба­
зовой библиотеки WinJS. В первом разделе вы узнали, как Java­
Scгipt-кoд организуется в виде набора модулей, пространств имен и классов. Благодаря модулям и пространствам имен удается избежать загрязнения глобального пространства имен и упростить сопровождение приложения. Далее мы обсудили, как писать асинхронный код с помощью обещаний. В частности, было показано, как использовать обещания Глава 2. Основы WinJS для выполнения Аjах-запросов. Вы научились создавать, отменять, сцеплять обещания и составлять новые обещания из существующих. Мы также обсудили использование селекторов запросов WinJS для выборки элементов DOM. Вы узнали, как с помощью функции WinJS. Uti l i ties. query ( ) выполнить запрос по селектору и полу­
чить результат в виде коллекции. Также вы узнали о функции WinJS. Uti l i ties. id ( ),которая отбирает единственный элемент. Наконец, я объяснил, как с помощью функции WinJS. xhr ( ) от­
правлять Аjах-запросы из приложения Магазина Windows. Вы узна­
ли, как параметр responseType позволяет задать тип данных, возвра­
щаемых функцией WinJS. xhr ( ), и, в частности, вернуть результат Аj ах-запроса в виде строки или документа. fJI AB A 3. На 611 ю дае м ые объ ек ты, nр и вязки и w а 611 о нь1 В этой главе: • Наблюдаемые объекты.
• Декларативная привязка к данным.
• Шаблоны.
В этой главе я объясню, как отображать объектыjаvаSсгiрt, например один товар или массив товаров, на странице приложения Магазина Windows. Я начну с наблюдаемых объектов ( observahl e ). Наблюдаемый объ­
ект позволяет автоматически обнаруживать изменение любого свойс­
тва. Я объясню также, как с помощью объекта WinJS. Binding. List обнаруживать, что изменились какие-то элементы массива. Затем мы перейдем к декларативной привязке к данным. Вы уз­
наете, как использовать обычные и наблюдаемые объекты JavaScгipt совместно с этим механизмом. Наконец, мы рассмотрим, как показать массив объектов с помо­
щью шаблона WinJS. Шаблон дает возможность форматировать и отображать сразу несколько объектов J avaScгi pt. Наблюдаем ые объ ект ы На блюдаемы.м называется объект, который может уведомлять одного или нескольких наблюдателей (прослушивателей) о том, что какое­
то его свойство изменилось. Наблюдаемые объекты позволяют син­
хронизировать данные приложения с пользовательским интерфей­
сом, например, автоматически обновлять интерфейс при изменении Глава 3. Наблюдаемые объекты, привязки и шаблоны свойств товара. Наблюдаемые объекты лежат в основе декларативной привязки к данным в библиотеке WinJS. Примечание. Wi nJS - не первая JаvаSсri рt-библиотека, поддерживающая наблюдаемые объекты. Этот механизм реализован также в библиотеках Backbone, Knockout, Ember, Mi crosoft Ajax Li brary (теперь часть Ajax Control Tool ki t) и других. Создание наблюдаемого объекта Рассмотрим такой объект, представляющий товар: var product = { name: 11 Mi lk11, } ; descript i on: "Something t o drink", price: 1 2.3 3 Ничего особенного. У объекта есть три свойства: name, des crip­
tion и price. А теперь допустим, что я хотел бы автоматически получать уве­
домление, когда любое из этих свойств изменится. В таком случае я создаю из обычного объекта товара наблюдаемый объект: var observaЬleProduct = WinJS.Binding.as ( product ); Здесь мы создаем новый объект JavaScгipt obs ervaЬleProduct из существующего объекта product. У нового объекта тоже есть свойст­
ва name, description и price. Но в ОТЛИLIИе от свойств исходного объекта, свойства наблюдаемого объекта генерируют уведомления при изменении. Каждое свойство нового наблюдаемого объекта обладает метода­
ми чтения и установки. Например, свойство price определено при­
мерно так: price: { get: funct ion ( ) { return thi s. get Property ( • pri c e"); } set: funct i on ( value ) { thi s. s et Property ( "price", value ) ; При чтении свойства price вызывается метод getProperty ( ),и при его установке - метод setProperty ( ). Методы getProperty ( ) и setProperty ( ) принадлежат наблюдаемому объекту product. Наблюдаемый объект product об ладает сле ду ющими свойс тва ми и методами: addProperty ( name, value) - добавляет новое свойство наб­
людаемому объекту и уведомляет прослушивателей; Наблюдаемые объекты ЬackingData - объект, в котором хранятся значения свойств; Ьind ( name, action) - позволяет выполнить какую-то функ­
цию при изменении свойства; getProperty ( name) - возвращает значение свойства с ука­
занным именем; notify ( name, newValue, oldValue) - закрытый метод, ко­
торый исполняет все функции в массиве _l i s teners; removeProperty ( name) - удаляет свойство и уведомляет прослушивателей; setProperty ( name, value) - изменяет значение свойства и уведомляет прослушивателей; unЬind ( name, action) - позволяет прекратить вызов ука­
занной функции в ответ на изменение свойства; updateProperty ( name, value) - изменяет значение свойства и уведомляет прослушивателей. Таким образом, результатом создания наблюдаемого объекта яв­
ляется новый объект с теми же свойствами, что у существующего. Од­
нако при модификации свойств наблюдаемого объекта все прослуши­
ватели автоматически уведомляются об изменении. Допустим, что значение свойства price изменилось в результате такого присваивания: observaЬleProduct.price = 2.9 9; В этом случае возникает следующая цепочка событий: Метод установки свойства price вызывает JVIeтoд setProper­
ty ("price", 2.9 9 ). Метод setProperty ( ) изменяет значение свойства backing­
Data .price и вызывает метод notify ( ). Метод noti fy ( ) по очереди вызывает все функции из кол­
лекции прослушивателей, ассоциированной со свойством price. При изменении свойства наблюдаемого объекта можно автома­
тически выполнить одну или несколько функций (прослушивате­
лей). Предупреждение. Попытка вызвать метод WinJS. Binding. as ( ) для объ­
екта Wi nRT приведет к исключению. Проблема в том, что объекты Wi nRT не­
изменяемые, а метод Wi nJ S. Bi n di ng. as ( ) пытаетсs:� добавить новый метод getObservaЫe ( ) , что для неизменs:�емого объекта, конечно, невозможно. Следовательно, объект JavaScri pt можно превратить в наблюдаемый, а объ­
ект Wi пRТ - нет. Глава 3. Наблюдаемые объекты, привязки и шаблоны Создание прослушивателей наблюдаемых объектов Если мы хотим получать уведомление о том, что некое свойство на­
блюдаемого объекта изменилось, то должны зарегистрировать про­
слушиватель. Для этого предназначен метод Ьind ( ) (листинг 3.1 ). Листинг 3.1. Привязка свойс тва объекта к nрослуши вателю ( observaЫes\observ aЫes. html ) 1 1 Обычный объект product var product = { } ; name: 11 Milk" / descript i on: "Something t o drink", price: 1 2.3 3 1 1 Создаем наблюдаемый объект product var observaЬleProduct = WinJS.Binding.as ( product }; 1 1 Выполнять функцию при всяком изменении цены observaЬl eProduct.Ьind ("price", funct ion ( newValue, ol dValue } { consol e.log ( newValue + "was "+ ol dValue ); } ) ; 1 1 Изменяем цену observaЬleProduct.price = 2.9 9; Здесь метод Ьind ( ) ассоциирует со свойством price функцию. Когда свойство price изменяется, эта функция выводит на консоль JavaScгipt новое и старое значение (рис. 3.1 ). 12. 33 1•as und�fined 2.9 9 �1as Н.3 3 » Рис. 3. 1. Привязка к свойству Ассоциирование функции со свойством price производится в следующих строчках: 1 1 Выполнять функцию при всяком изменении цены observaЬleProduct.Ьind ("price", funct i on ( newValue, oldValue } { console.l og ( newValue + " was " + ol dValue }; } } ; Наблюдаемые объекты �· · ••ID Отметим, что функция, привязанная к свойству price, вызывает­
ся дважды: при первом присваивании значения свойству и при его изменении. Примечание. Для привязки прослушивателя к составному свойству необхо­
димо в качестве второго параметра метода Wi nJ S. Bi n di ng. Ьi nd ( ) указать объект, как показано в коде ниже. 1 1 Создать объект, соответствующий составному свойству var cus t omer = { shippingAddress: street: "3 1 2 Main Street • }; 1 1 Создать наблюдаемый объект var observaЬleCustomer = WinJS.Binding.as ( customer ); 1 1 Привязать к составному свойству WinJS.Binding.bind ( observaЬleCustomer, shippingAddres s: { } 1; street: funct i on ( newValue ) { cons ol e.l og ("Modi f i ed shipping address to • + newValue ) ; 1 1 Изменить значение составного свойства observaЬleCustomer.shippingAddress.street = "1 0 0 Grant Street • Объединение уведомлений Если свойство последовательно изменяется несколько раз, то прослу­
шивателям посылается всего лишь одно уведомление, то есть уведом­
ления объединяются. Например, в листинге 3.2 свойство price изменяется три раза под­
ряд. Однако на консоль выводится только два сообщения - о началь­
ном и о последнем присваивании значения. Ли сти нг 3.2. Объединение уведомлений ( observaЫesCoalesce\observabl esCoalesce.html ) 1 1 Обычный объект product var product = { name: "Milk", descript i on: • something t o drink", Глава 3. Наблюдаемые объекты, привязки и шаблоны price: 1 2.3 3 } ; 1 1 Создаем наблюдаемый объект product var obs ervaЫeProduc t = WinJS.Binding.as ( product ); 1 1 Выполнять функцию при всяком изменении цены observaЫeProduct.Ьind ("pri ce", funct ion ( newValue, oldValue ) { consol e.l og ( newValue + "was "+ ol dValue ); } ) ; 1 1 Изменяем цену observaЫeProduct.pri ce obs ervaЬleProduct.pri ce observaЬleProduct.pri ce 3.9 9; 2.9 9; 1.9 9; 12. З3 1·1as undefiлed 1.99 VIOS 2.99 » Рис.3.2.Объединениеуведомлений Если бы между последовательными изменениями свойства про­
шло какое-то время, то были бы отправлены отдельные уведомле­
ния. Если требуется предотвратить объединение уведомлений - и при этом не создавать искусственную задержку, - то можно воспользо­
ваться обещаниями. Поскольку метод updateProperty ( ) возвраща­
ет обещание, мы можем следующим образом породить различные уведомления для каждого изменения свойства: 1 1 Изменяем цену observaЫeProduct.updateProperty ("price", 3.9 9 ) .then ( funct i on ( ) { obs ervaЫeProduct.updateProperty ("pri ce", 2.9 9 ) .then ( funct ion ( ) { } ) ; observaЬleProduct. updateProperty ( "pric e", 1. 9 9 ); } ) ; В этом случае посылаются отделъные уведомлении о каждом из­
менении свойства, несмотря на то LПО изменения следуют друг за другом без задержюr (рис. 3.3). Наблюдаемые объекты Х Clear :О О Errors i !� O\;l�r�,��;: »1 12..З. З was i;nd�Tiлe� З.99 ''<a.s 12.33 2.99 't<a:S З.99 1.99 1.-Jas 2.99 Рис. 3.3. Использование обещаний совместно с наблюдаемыми объектами Обход отправки уведомлений Обычно, если со свойством наблюдаемого объекта ассоциированы прослушиватели, то при всяком изменении свойства им посылается уведомление. Но бывают случаи, когда желательно не посылать уве­
домление, то есть изменить свойство «по-тихому1> - не вызывая заре­
гистрированные функции. Чтобы обойтись без отправки уведомлений, следует изменять свойство объекта backingDa ta. В листинге 3.3 показано, как изме­
нить свойство price <:< по-тихому1>. Лис тинг З.З. Обход уведо млений (observaЫesB ypass\observaЫesBy pass.html) //Обычный объект product var product = { }; name: "Mi l k", descript i on: "Something t o drink", price: 1 2.3 3 // Создаем наблюдаемый объект product var observaЬleProduct = WinJS.Binding.as ( product ); //Выполнять функцию при всяком изменении цены observaЬleProduct. bind ( "pri ce", funct i on ( newValue ) consol e.log ( newValue ); } ) ; // Изменяем цену «по-тихому» observaЬl eProduct.backingData.pri ce 5.9 9; consol e.log ( observaЬl eProduc t.pri ce ); //Выводится 5.9 9 Мы записали в цену новое значение 5.99 путем изменения свойст­
ва backingData.price. Поскольку свойству observaЬleProduct. price значение напрямую не присваивалось, то и ассоциированным с ним прослушивателям уведомление не отправлялось. " ••.. Глава 3. Наблюдаемые объекты, привязки и шаблоны Если значение свойства изменяется с помощью объекта backing­
Da ta, то изменение производится синхронно. Если же значение на­
блюдаемого свойства производится напрямую, то это делается асинх­
ронно. Работа с объектом WinJS.Binding.List Если вы хотите получать уведомление при всяком изменении масси­
ва объектов, то есть работать с наблюдаемой коллекцией, то следует использовать объект WinJS. Binding. Li s t. Объект WinJS. Binding. Li s t обертывает стандартный массив JavaScгipt, добавляя новые методы и события для поддержки уведом­
лений об изменении. Поддерживаются следующие события: i teminserted- генерируется при добавлении нового элемента в список; i temchanged - генерируется при замене элемента списка методом setAt ( ); i temmoved - генерируется при перемещении элемента списка методом move ( ) ; itemmutated - генерируется при вызове метода notifyMu­
tated ( ); i temremoved - генерируется при удалении элемента из списка; reload - генерируется при изменении порядка элементов в результате вызова метода sort ( ) или reverse ( ). Также генерируется при вызове метода noti fyReload ( ). В листинге 3.4 демонстрируется генерация вышеперечисленных событий. Показано также, какая информация передается обработчи­
ку каждого события. Листинг 3.4. Исполь зован ие объе кта Wi nJS. Bi ndi ng.Li st ( observaЫesList\ observaЫesList. html ) var product s = [ { name: "Milk" , pri ce: 2.9 9 }, { name: "Oranges" , price: 2.5 0 }, { name: "Appl e s" , pri ce: 1.9 9 } ] ; 1 1 Создаем список var products Li s t = new WinJS.Binding.Li s t ( product s ); 1 1 Настраиваем обработчики событий Наблюдаемые объекты 1 ••• llDll product s Li s t.onit emins erted = func t i on ( evt ) var mes s age = "It em Inserted: " + evt.detai l.value.name + " at index " + evt.detai l.index } ; + " with key " + evt.detai l.key; cons ol e.l og ( mes sage ); product s Li s t.oni ternchanged = func t i on ( evt ) var mes s age = "It em Changed: " + evt.detai l.oldValue.name + " to " + evt.det ai l.newValue.name + " at index " + evt.detai l.index + " with key " + evt.detai l.key; consol e.log ( mes sage ); } ; product s Li s t.onitemmutated = funct i on ( evt ) var mes sage = "I tem Mut ated: " + evt.detai l.value.name + " wi th key " + evt.detai l.key; cons ol e.log ( mes s age ); } ; product s Li s t.oni t emremoved = func t i on ( evt ) { var mes s age = "It em Removed: " + evt.detai l.value.name + " at index " + evt.detai l.index + " with key " + evt.detai l.key; consol e.log ( mes s age ); }; product s Li s t.onit emmoved = funct i on ( evt ) var mes s age = "I tem Moved: " + evt.detai l.value.name + " from index " + evt.detai l.oldindex + " to index " + evt.detai l.newindex; consol e.log ( mes s age ); } ; product s Li s t.onrel oad = func t i on ( evt ) var mes s age = "Li s t Reloaded"; consol e.log ( mes s age ); } ; //Вставляем объект. Генерируется i t eminserted product s Li s t.push ( { name: "Carro t s" , pri ce: 2.3 3 } ); 1 1 З аменяем объект целиком. Генерируется i temchanged product s Li s t. s etAt ( 1, { name: "Navel Oranges" , price: 2. 5 О } ) ; 1 1 Обновляем свойство объекта. product sLi s t.getAt ( l ) .price = 5 0 0.0 0; product s Li s t.noti fyMutated ( l ); // Генерируется it emmutated //Удаляем объект. Генерируется i t emremoved " ••••• , Глава 3. Наблюдаемые объекты, привязки и шаблоны products Li s t.spl i ce ( O, 1 ); 1 1 Перемещаем второй элемент в начало списка. Генерируется itemmoved productsLi s t.move ( l, 0 ); 1 1 Сортируем список. Генерируется rel oad products Li s t.s ort ( ); В листинге 3.4 создается массив JavaScript с именем products, ко­
торый представляет список товаров, имеющих свойства name и price. Затем из этого массива создается объект WinJS. Binding. Lis t: var product s Li s t = new WinJS.Binding.Li s t ( product s ); Далее для всех событий WinJS. Binding. Li s t создаются обработ­
чики. Каждый обработчик события выводит сообщение на консоль JavaScгipt в Visual Studio (рис. 3.4) . »1 . !1-О l'J�Гfling•J О 3 Мешg" Iter.i Inser-ted: C.arrots ;st index 3 t'i'ith key 4 Itern Ch ci;ged: Orвnges to Na:vel Oгanges at ir.dex 1 with key 1 ILer.i Nu·tateo: f'la'.>el Ora:r>ges '4:it h key 1 Itern Remo ved: Milk at in-dex 0 YJith k:�y 0 Itern No\1ed: Apple::o fro;n index 1 t-o index 0 list Reload t!!d Рис. 3.4. События объекта WinJS. Binding. List Создание наблюдаемой коллекции наблюдаемых объектов По умолчанию при создании объекта WinJS. Binding. List из масси­
ва JavaScript сам список является наблюдаемым, но содержащиеся в нем объекты - нет. Объект WinJS. Binding. Lis t просто содержит элементы исходного массива, которые являются обычными объекта­
ми J avaScгipt. Чтобы преобразовать все элементы мaccивaJavaScгipt в наблюда­
емые объекты, нужно при создании WinJS. Binding. Lis t задать па­
раметр binding: var product s = [ { name: "Milk", price: 2. 9 9 } , { name: "Oranges", price: 2.5 0 }, { name: "Apples", price: 1.9 9 } Привязка к данным ••• ••llDI ] ; //Создаем список var produc t sL i s t = new WinJS.Bi nding.L i s t ( produc t s, ( bi n ding: t r ue ) ); //Прослушиваем изменения цены product s Li s t. getAt ( 1 ) . bind ( "price", funct i on ( ) { consol e.log ("price changed"); ) ) ; Здесь productsLi s t содержит список наблюдаемых объектов. Поскольку каждый объект в списке наблюдаемый, можно присоеди­
нить к нему функцию-прослушиватель, которая будет вызываться при изменении свойства. В примере выше эта функция выводит со­
общение на консоль JаvаSсгiрt при изменении свойства price. Привязк а к данным Механизм декларативной привязки к данным позволяет привязы­
вать атрибуты элемента I-ITML к свойствам объектаjаvаSсгiрt. Этим механизмом можно воспользоваться для отображения данных на НТМL-странице. Начнем с простого примера. На странице, показанной в листин­
ге 3.5, отображаются сведения о товаре. Листинг 3.5. Простой пример декларативной привязки к данным ( dataBinding\dataBinding.html ) <!DOCTYPE html> <html > <head> <meta charset ="ut f - 8" /> <t i t l e>Chapter0 3 </t i t le> <!- - Ссылки на WinJS - - > <l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui -dark.cs s" 4re l ="s tylesheet" /> <script src="//Micros of t.WinJS.1.0/j s/base.j s"></s cript> <script src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 3 - - > < l ink hre f ="/c s s/def aul t.c s s" rel ="stylesheet" /> <script src="/dataBinding/dataBinding.j s"></script> </head> cbody> <hl>Product Detai l s </hl > Глава 3. Наблюдаемые объекты, привязки и шаблоны <div> Product Name: <span data-win-Ьind="innerText:name"></span> </div> <div> Product Pri ce: <span data-win-bind="innerText:pri ce"></span> </div> <div> Product Picture: <br /> < img data-win-bind="src:photo;al t:name" /> </div> </body> </html> Название, цена и изображение товара отображаются внутри эле­
ментов SPAN. Отметим, что у каждого такого SPAN'a имеется атрибут data-win-Ьind. Вот, например, как выглядит элемент SPAN, в кото­
ром отображается название товара: <span data-win-Ьind="innerText:name"></span> Атрибут data-win-Ьind как раз и связывает значение свойства name со свойством innerText элемента SPAN. Атрибут data-win-Ьind позволяет привязать (почти) любой ат­
рибут НТМL-элемента к значению свойства jаvаSсгiрt-объекта. На­
пример, изображение Теслы появляется в результате привязки атри­
бутов src и al t элемента I MG: < img data-win-bind="src:photo;al t:name" /> Отметим, что значением атрибута data-win-Ьind может быть список разделенных точкой с запятой пар «имя атрибута НТМL-эле­
мента - имя свойстваjаvаSсгiрt-объекта�. Примечание. Один из атрибутов, которые невозможно привязать декла­
ративно, - это атрибут I D. По умолчанию библиотека Wi nJS автоматически генерирует уникальный I D для всех элементов. Это поведение можно подавить, присвоив значение f al se свойству opti mi zeBi n di ngR e f er enc­
e s. В листинге 3.6 приведено описание товара, который отображается на НТМL-странице из листинга 3.5. Лист инг 3.6. Простой пример декларативной привязки к данным (datablndi ng\databl ndi ng.js) 1 funct i on ( ) { 11 use stri ct"; Привязка к данным funct i on ini t i al i z e ( ) var product = { name: "Tes l a Roads t er", price: 3 4, photo: "t es l a. j pg" } ; WinJS.Binding.proces sAl l ( nul l, product ); } document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ) ; Объект product в листинге 3.6 - это обычный объект JavaSci-ipt, ничего особенного в нем нет. Рис. 3.5. Отображение сведений о товаре с помощью декларативной привязки к данным Но обратите внимание на вызов метода WinJS. Binding. proces sAll ( ).Атрибуты, описывающие декларативную привязку к данным, обрабатываются только после вызова этого метода. Метод WinJS. Binding. procesAll ( ) принимает два параметра: корневой элемент и контекст данных. Корневой элемент определяет, какие элементы страницы обраба­
тываются. Если передано значение null, то анализируется весь до­
кумент. Контекст данных содержит данные, к которым привязываются НТМL-атрибуты. В листинге 3.6 в качестве контекста данных пере-
Глава 3. Наблюдаемые объекты, привязки и шаблоны дается объект product. Предупреждение. Не вызывайте метод Wi nJ S. Bi ndi ng. pr oces sAl l ( ) до окончания загрузки всего документа, иначе ему будет просто нечего обра­
батывать. В листинге 3.6 обращение к этому методу находится в функции, которая вызывается из обработчика события DOMCont ent Loaded. Декларативная привязка к данным и наблюдаемые объекты Сочетание декларативной привязки к данным и наблюдаемых объ­
ектов - мощное средство. Оно позволяет автоматически обновлять содержимое НТМL-документа при всяком изменении JаvаSсгiрt­
объектов, от которых он зависит. Например, на НТМL-странице из листинга 3.7 имеются элементы SPAN и BUTTON. На странице отображается, сколько раз была на­
жата кнопка (рис. 3.6). You have clкked the button 3 trmes. � Рис. 3.6. Отображение счетчика нажатий Лис тинг 3. 7. Использование привязки к данным совместно с наблюдаемыми объектами ( dat aBindingObs ervaЫes\ dataBi ndingObser va Ыes. html ) <!DОС'ГУРЕ html > <html > <head> <meta charset ="ut f - 8" /> <t i t l e> ChapterOЗ </t i t l e> <!- - Ссылки на WinJS - - > <l ink hre f ="//Microso f t.WinJS.1.0/c s s/ui -dark.c s s" '+rel ="s tyl esheet" /> <s cript src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <script src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на ChapterO З - - > < l ink hre f ="/c s s/def aul t.cs s" rel ="s tylesheet" /> <script src ="dataBindingObservaЫes.j s"></script> </head> <body> Привязка к данным l l B ••IШI <div> You have c l i cked the but t on <span data-win-bind="innerText:t imesCl icked"></span> t imes. <br /> <button data-win-bind="onc l i c k:c l i ck">Cl i ck Here!</but t on> </div> </body> </html > В листинге 3.7 встречается два атрибута data-win-Ьind. Пер­
вый - в элементе SPAN - служит для отображения количества нажа­
тий, второй - в элементе BUTTON - для обработки события щелчка МЫШЬЮ. JavaScгipt-кoд в листинге 3.8 содержит наблюдаемый объект viewModel, в котором подсчитываются нажатия кнопки. Лис ти нг 3.8. Использование привязки к данным совместно с наблюдаемыми объектами ( dataBi ndingObservables\dataBindingObserv aЫes.html) ( funct ion ( ) { "use stri ct"; funct i on ini t i al i z e ( ) // Создаем модель представления var viewModel = { t imes Cl i cked: О, c l ick: funct i on ( evt ) evt.preventDefaul t ( ); viewModel.t imes Cl i cked++; }; // Делаем модель представления наблюдаемым объектом viewModel = WinJS.Binding.as ( vi ewModel ); // Привязываем модель представления к документу WinJS.Binding.proces sAl l ( nul l, vi ewModel ); document.addEvent Li s t ener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); У объекта viewModel имеется свойство times Cli cked для подсче­
та количества нажатий на кнопку. Также у него есть метод click ( ), в котором обновляется свойство t imes Clicked. Объект viewModel преобразуется в наблюдаемый объект методом WinJS. Binding. as ( ).И наконец, с помощью метода WinJS. Binding. Глава 3. Наблюдаемые объекты, привязки и шаблоны proces sAll ( ) мы привязываем viewModel к странице и обрабатыва­
ем атрибуты data-win-Ьind. Получение содержимого НТМL-формы Библиотека WinJS не поддерживает двустороннюю привязку к дан­
ным. Уведомление об изменениях работает только в одну сторону. Если вы хотите получить содержимое НТМL-формы, то должны чи­
тать значения ее элементов самостоятельно. Страница из листинга 3.9 содержит НТМL-форму для создания нового товара. В ней есть два элемента INPUT для ввода названия и цены (рис. 3.7). В самом элементе FORM имеется атрибут data-win­
Ьind, который присоединяет обработчик события отправки. Рис. 3.7. Создание нового товара Лист инг 3.9. Д вусто рон няя при в я з ка к д а нным (dataBindingTwoWay\d ataBindingTwaWay.html ) <!DOCTYPE html> <html> <head> <meta charset ="ut f - 8" /> <t i t le>Chapter0 3 </t i t le> <!- - Ссылки на WinJS - - > <l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui - dark.c s s" �rel ="s tyl esheet" /> <script src="//Microsof t.WinJS.1.0/j s/base.j s"></script> <script src="//Microso f t.WinJS.l.O/j s/ui.j s"></script> <!- - Ссыпки на Chapter0 3 - - > < l ink hre f ="/c s s/def ault.c s s" rel ="s tylesheet" /> <script src ="dataBindingTwoWay.j s"></script> </head> <body> < form data-win-Ыnd="onsubmi t:submi t"> <div c l as s ="f i el d"> Привязка к данным 111••m1 <l abel >Name:</l abel > <input id="productName" required /> </div> <div cl as s ="f ield"> <l abel>Pri ce:</l abel > < input id="productPri ce" required /> </div> <div c l as s ="f i el d"> <butt on>Add Product</but t on> </div> </form> </body> </html> В момент отправки НТМL-формы вызывается метод viewData. submit ( ) (листинг 3.1 0). Он извлекает значения из полей формы и создает новый объект productToAdd. Листинг З. 1 О. Двусторонняя привязка к данным ( dataBindingTwoWay\d ataBindingTwoW ay.js) 1 funct i on ( ) { "use stri ct"; funct i on ini t i al i z e ( ) var vi ewModel = { } ; submit: funct i on ( evt ) { // Предотвратить отправку страницы evt.preventDe f aul t ( ); 1 1 Получаем значения полей формы var productToAdd = { name: document.getElementByi d ("productName") .value, price: document.getElementByi d ("productPrice") .value } ; // TODO: добавить новый продукт в базу данных WinJS.Binding.proces sAl l ( nul l, vi ewModel ); document.addEventLi s t ener ("DOMCont entLoaded", ini t i al i z e ); } ) ( ) ; На странице из листинга 3.9 атрибут data-win-Ьind использует­
ся для установки обработчика события отправки. Может возникнуть m1••8ll Глава 3. Наблюдаемые объекты, привязки и шаблоны запрос, не проще ли обойтись без привязки к данным, а задать обра­
ботчик напрямую, вот так: < form onsubmi t ="submi t"> Зачем вообще нужна декларативная привязка к данным? Ее досто­
инство в том, что обработчик события не присутствует в пространстве имен. В этом случае он не обязан быть открытым методом, требуется лишь, чтобы он был частью контекста данных, передаваемого методу WinJS.Binding.proces sAl l ( ). Примечание. В НТМL-форме из листинга 3.9 используется появившийся в HTML5 атрибут req ui red, который гарантирует, что название и цена введе­
ны. Декларативная привязка к данным и элементы управления WinJS Элементы управления WinJS подробно обсуждаются в следующей главе. Но уже сейчас я хочу, чтобы вы понимали, что декларативную привязку к данным можно использовать для привязки к свойствам элемента управления - точно так же, как к атрибутам элемента. Хитрость в том, чтобы совместно с атрибутом data-win-Ьind ис­
пользовать свойство winControl, которое имеется у каждого НТМL­
элемента, ассоциированного с элементом управления. Так, на странице из листинга 3.1 1 присутствует элемент управле­
ния Wi11JS Rati11g (рис. 3.8). �: ��fw_A '1:.r "'}:: А;;: The average rating for this product i s 3 1 Рис. 3.8. Отображение среднего рейтинга с помощью декларативной привязки к данным Лис тинг 3. 1 1. Использование декларативной привязки к данным с элементом управления Wi nJS ( dat aBindingControls\data BindingContr ols. html ) <!DOCTYPE html> <html > <head> <meta charset ="ut f - 8" /> <t i t l e>Chapter0 3 </t it l e> <!- - Ссыпки на WinJS - - > < l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui - dark.cs s" Привязка к данным 4re l ="s tylesheet" /> cscript src="//Microso f t.WinJS.1.0/j s/base.j s"></script> cscript src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 3 - - > cl ink hre f ="/c s s/def aul t.c s s" rel ="styl esheet" /> cscript s rc ="dataBindingContro l s.j s"></script> c/head> >body> cdiv dat a-win- control ="WinJS.UI.Rat ing• data-win-bind="winControl.averageRat ing:averageRat ing"> </div> cdiv> The average rat ing for thi s product i s: >span data-win-bind="innerText:averageRating"></span> </div> c/body> c/html> Здесь свойство averageRating элемента управления Rating уста­
навливается с помощью следующего атрибута data-win-Ьind: data-win-bind="winControl.averageRat ing:averageRat ing" Свойство winControl позволяет перейти от НТМL-элемента к ассоциированному с ним элементу управления. В листинге 3.1 2 показано, как средний рейтинг привязывается к элементу Rating. Листинг 3. 1 2. Использование декларативной привязки к данным с элементом управления Wi nJS (dataBindingControls\dataBindingControls.js) ( funct i on ( ) { 11 use stri ct"; funct i on ini t ial i z e ( ) // Создаем модель представления var viewMode l = { averageRat ing: 3 } ; // Привязываем модель представления к документу WinJS.UI.proces sAl l ( ) . done ( func t i on ( ) { WinJS.Binding.proces sAl l ( nul l, vi ewModel ); } ) ; IППl • • l l l Глава 3. Наблюдаемые объекты, привязки и шаблоны document.addEventLi s tener ("DOMContentLoaded", ini ti al i z e ); } ) ( ); В листинге 3.1 2 есть два обращения к методам proces sAl l ( ) из разных пространств имен. Первый метод вызывается для обработки всех элементов управления WinJS на странице, а второй - для обра­
ботки всех атрибутов привязки к данным. Эти обращения должны следовать именно в таком порядке, иначе на странице не будет эле­
ментов управления, к свойствам которых можно было бы привязать­
ся. Декларативная привязка к данным и конвертеры привязки Конвертер привязки позволяет преобразовывать значение свойства, упомянутого в атрибуте data-win-Ьind. Конвертеры бывают полезны в самых разных ситуациях, напри­
мер, для форматирования даты и времени или для сокрытия либо по­
каза содержимого в зависимости от значения свойства. Всякий раз как необходимо изменить значение некоторого свойства JаvаSсгiрt­
объекта перед его показом, на помощь приходит конвертер. Пусть, к примеру, требуется показывать текст «On Sale!» ( Рас­
продажа) только для товаров со сниженной ценой. В листинге 3.1 3 приведена страница, на которой отображаются два товара (рис. 3.9). Чтобы скрыть или показать содержимое элемента DIV с текстом «On Sale!», применяется конвертер. Рис. 3.9. Использование конвертера привязки Листинг 3.1 3. Использование конвертера привязки (dataBindingConverters/dataBindingConve rters.html ) <!DOCTYPE html> <html > <head> Привязка к данным <meta charset ="ut f - 8" /> < t i t l e>Chapter0 3 </t i t le> <!- - Ссылки на WinJS --> < l ink href ="//Micros of t.WinJS.1.0/c s s/ui-dark.cs s" 4re l ="styl esheet • /> < script src ="//Micros of t.WinJS.1.0/j s/base.j s"></script> <s cript src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на ChapterO З - - > < l ink hre f ="/c s s/def aul t.c s s" rel ="s tylesheet" /> <s cript src ="dataBindingConvert ers.j s"></script> <s cript type="t ext/j avascript" s rc = • myBindingConvert ers.j s"> 4</s cript> </head> <body> <div> <hl data-win-Ьind="innerText:produc t l.name"></hl> <div> Pri ce: <span data-win-Ьind="innerText:produc t l.pri ce"> 4</span> </div> <div data-win-Ьind="style.display:produc t l.onSale 4MyBindingConverters.onSal eToDisplay"> <Ь> Оп Sale! </Ь> </div> </div> <div> <hl data-win-Ьind="innerText:product 2.name"></hl> <div> Price: < span dat a-win-bind="innerText:product 2.pri ce"> 4</span> </div> <div data-win-Ьind="s tyl e.display:product 2.onSale 4MyBindingConverters.onSal eToDisplay"> <b>On Sale!</b> </div> </div> </body> </html> После первого товара текста «On Sale!� нет, а после второго - есть. Этот текст отображается благодаря такому элементу DIV: <div data-win-Ьind="s tyl e.display:produc t 2.onSale 4MyBindingConverters.onSaleToDisplay"> <b>On Sale!</b> </div> Конвертер привязки применяется к атрибуту style элемента DIV. Ш1 • • 11r; Глава 3. Наблюдаемые объекты, привязки и шаблоны Когда конвертер возвращает значение «none>>, содержимое элемента DIV скрывается (потому что для него задан стиль display: none). Если же конвертер возвращает значение «Ьlock», то содержимое эле­
мента D IV показывается. Конвертер привязки указывается в атрибуте data-win-Ьind пос­
ле имени привязываемого свойства. В данном случае конвертером яв­
ляется функция MyBindingConverters. onSaleToDi splay. Этот кон­
вертер преобразует булевское значение в строку «none» или «Ьlock». Конвертер привязки находится в отдельном файле myBindingCon­
verters. j s, на который ссылается НТМL-страница (листинг 3.1 4). Листинг 3. 1 4. Конвертер привязки ( dataBindingConverters\myB indingConverter s.js) ( funct ioп ( ) { 11 use stri ct"; var onSaleToDi splay = WinJS.Binding.converter ( functioп ( oпSale ) { returп oпSale ? "Ьlock" : • попе •; } ) ; WiпJS.Namespace.de f iпe ("MyBiпdiпgConvert ers •, { oпSal eToDi splay: onSal eToDi splay } ) ; } ) ( ); Чтобы создать конвертер привязки, нужно передать функцию методу WinJS. Binding. converter ( ) . Эта функция преобразует пе­
реданное ей значение во что-то другое. В примере выше булевское свойство товара oпSale преобразуется в строку «Ьlock» или -«llone», которая затем используется в качестве значения свойства display в атрибуте style. В листинге 3.1 5 создаются два товара: product l и product2, пред­
ставленные в виде свойств объекта viewModel. Листинг 3.1 5. Использование конвертера привязки ( data BindingConverters/data BindingConverter s.js) ( fuпct i oп ( ) { "use stri ct"; fuпct i oп iпi t i al i z e ( ) var viewModel = ( produc t l: { name: 11 Tesl a11, pri ce: 3 0 0 0 0 0.0 0, oпSale: f al se Привязка к данным } ' product 2: } ; name: "BMW", pri ce: 8 0 0 0 0.0 0, onSale: true l l l ••IDI WinJS.Binding.proces sAl l ( nul l, vi ewModel ); document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Создание конве рте ров привяз ки для преобр аз ования дат ы и де нежн ой суммы Не могу закончить этот раздел, не упомянув еще два типа конвер-
теров: даты и денежной суммы. Практически ни одно из моих прило­
жений Магазина Windows не обходится без них. В листинге 3.1 6 показаны оба этих конвертера. Листинг 3. 1 б. Конвертеры даты и денежной суммы ( funct i on ( ) { 11 use strict"; //Преобразует 7 7.8 9 0 0 в $ 7 7.8 9 var price = WinJS.Binding.converter ( function ( priceToConvert ) { return "$" + priceToConvert.toFixed ( 2 ); } ) ; 1 1 Преобразует полную дату в 1 2/2 5/2 0 1 3 var shortDate = WinJS.Binding.converter ( function ( dateToConvert ) { return dateToConvert.getMonth ( ) + 1 + "/" + dateToConvert.getDate ( ) + "/" + dat eToConvert.getFul lYear ( ); } ) ; WinJS.Namespace.de f ine ("MyBindingConverters", { price: price, shortDate: shortDate } ) ; } ) ( ); Добавив ссылку на эти конвертеры на НТМL-страницу, вы смо­
жете использовать их в выражениях привязки, как, например, в лис­
тинге 3.1 7. Листинг 3. 1 7. Использование конвертеров даты и денежной суммы 11EJ•• l l f Глава 3. Наблюдаемые объекты, привязки и шаблоны <div> <hl dat a-win-Ьind="innerText:produc t l.name"></hl > <div> Price: <span data-win-Ьind="innerтext:productl.price MyBindingConverters. '+pri ce" ></ span> </div> <div> Date Avai l aЫe: < span dat a-win-Ьind="innerText:produc t l.dateAvai l aЫe '+MyBindingConvert ers.shortDat e"></span> </div> </div> Конвертер денежной суммы выводит цену $1 00.00 вместо 99.999999. Конвертер shortDate преобразует свойство dateAvail ­
aЫe товары к виду 1 2/25/201 2 вместо Tue Dec 25 00:00:00 PST 201 2. Примечание. Как это ни печально, конвертеру привязки нельзя передать дополнительные параметры. Как хорошо было бы, к примеру, передать па­
раметр, определяющий формат даты. Увы, не получится. Вместо этого при­
ходится создавать отдельные конвертеры da t eShor t и dateLong. Шаблоны Если требуется несколько раз поместить на страницу один и тот же фрагмент HTML, то имеет смысл создать шаблон. В состав шаблона могут входить выражения декларативной привязки к данным. Есть два способа создания шаблона: императивный и декларативный. Императивное создание шаблона Пусть требуется показать на НТМL-странице массив товаров. Тог­
да для представления каждого товара можно использовать шаблон (рис. 3.1 0). Рис. 3.1 О. Отображе ние списка товаров с помощью шаблона Шаблоны На НТМL-странице из листинга 3.1 8 присутствует элемент DIV с идентификатором tmpl Product. На отрисованной странице его не вид­
но. Его единственное назначение - хранить содержимое шаблона. На этой странице есть также DI V с идентификатором conProd­
ucts. Он используется как мишень шаблона - все отрисовываемые товары оказываются внутри этого элемента. Листинг 3.1 8. Императивное создание шаблона (templateslmperative\te mplateslmperative.html ) <!DOCTYPE html > <html> <head> <meta charset ="ut f - 8" /> <t i t l e> Chapter03 </t i t le> <!- - Ссылки на WinJS - - > <l ink hre f ="//Microsof t.WinJS.1.0/c s s/ui-dark.c s s • �re l ="s tyl esheet" /> <script src="//Microsof t.WinJS.1.0/j s/base.j s"></script > <script src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссыпки на Chapt er0 3 - - > < l ink hre f ="t emplates imperat ive.cs s • rel ="styl esheet" /> <script src="t emplates imperat ive.j s"></script> </head> <body> <!- - Шаблон - - > <div id="tmpl Product"> <div c l as s ="produc t"> Name: <span data-win-bind="innerText:name"></span> <br /> Price: <span data-win-bind="innerText:pri ce"></span> </div> </div> <!- - Здесь отрисовывается шаблон - - > <div i d="conProduc t s"></div> </body> </html > J avaScгi рt-код в листинге 3.1 9 показывает, как создать новый шаб­
лон и как отрисовать шаблон для каждого элемента массива. Листинг 3.1 9. Императивное создание шаблона (templateslmperative\templ ateslmperative.js) ( funct i on ( ) { 11 use stri ct"; Глава 3. Наблюдаемые объекты, привязки и шаблоны func t i on ini t i al i z e ( ) { var product s = [ ] ; { name: "Tes l a" , price: 3 0 0 0 0 0 } , { name: "BMW" , pri ce: 8 0 0 0 0 } , { name: "Pint o" , price: 1 0 0 0 0 } 1 1 Получить шаблон и контейнер шаблона var tmpl Product document.getElementByid ("tmpl Product"); var conProduct s = document.getEl ementByid ("conProduc t s"); 1 1 Создать шаблон var template = new WinJS.Binding.Templ ate ( tmpl Product ) 1 1 Отрисовать все элементы массива по шаблону produc t s.f orEach ( funct i on ( product ) { template.render ( produc t, conProduct s ); } ) ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Шаблон представлен экземпляром элемента управления WinJS Template: var template = new WinJS.Binding.Templ at e ( tmpl Product ) После того как шаблон создан, он применяется для отрисовки всех элементов массива внутри метода forEach ( ): product s. f orEach ( funct ion ( product ) { template.render ( product, conProduct s ); } ) ; При этом для каждого элемента отрисовывается только внутрен­
нее содержимое шаблона, а объемлющий элемент DIV (выше он на­
зван tmplProduct) опускается. Декларативное создание шаблона При желании можно создавать шаблон декларативно, а не импера­
тивно. В листинге 3.20 показан элемент управления WinJS Template, ассоциированный с элементом DIV tmpl Product. Лис т инг 3.20. Декларативное создание шаблона (templatesDeclare\templ atesDecl are.html ) <!DOCTYPE html > <html> Шаблоны <head> <meta charset ="ut f - 8" /> <t i t le> ChapterOЗ </t i t le> <!- - Ссылки на WinJS - - > <l ink hre f ="//Microsof t.WinJS.1.0/c s s/ui -dark.c s s" '+re l ="s tyl esheet" /> <s cript src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <s cript src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на ChapterO З - - > < l ink hre f ="t emplatesDeclare.c s s" rel ="s tylesheet" /> <script src="t emplatesDec lare.j s"></script> </head> <body> <!- - Шаблон - - > <div id="tmpl Product • data-win-control="WinJS.Binding.Templat e"> <div c l as s ="produc t"> Name: <span data-win-Ьind="innerтext:name"></span> <br /> Price: <span dat a-win-bind="innerText:pri ce"></span> </div> </div> <!- - Здесь отрисовывается шаблон - - > <div i d="conProduc t s"></div> </body> </html> Здесь в объявлении элемента управления Temp1ate участвует атрибут da ta-win-control="WinJS. Binding. Templa te". В листинге 3.21 приведен код отрисовки массива товаров по шаб­
лону. Листинг З.21. Декларативное создание шаблона (templ atesDeclare\templ atesDecl are.js) ( funct i on ( ) { 11 use stri ct"; funct i on ini t i al i z e ( ) var product s = [ ] ; { name: "Tes l a", price: 3 0 0 0 0 0 }, { name: • в мw ·, price: 8 0 0 0 0 } , { name: • Pinto", price: 1 0 0 0 0 } // Получить шаблон и контейнер шаблона var tmpl Product document.getElementByid ("tmpl Product"); var conProduct s = document.getElementByid ("conProduc t s"); 1111 •• 1 1" Глава 3. Наблюдаемые объекты, привязки и шаблоны 1 1 Отрисовать все элементы массива по шаблону WinJS.UI.proces sAl l ( ).done ( funct i on ( ) { product s.f orEach ( funct ion ( product ) { tmpl Product.winControl.render ( product, conProduct s ); } 1; } 1; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e l; } ) ( ); Когда используется декларативный элемент управления Template, необходимо вызывать метод WinJS. UI. proces sAl l ( ). В противном случае элемент DIV не будет преобразован в Template. Шаблоны и селекторы запросов В классе QueгyCollection имеется метод template ( ), который позволяет применить шаблон к одному или нескольким элементам DOM. Например, на странице из листинга 3.22 отображается список товаров. Ли сти нг 3.22. Использование метода WinJS. Utili ties. id ( ) совместно с шаблоном (templatesQuer y\te mplatesQuery.html ) <!DOCTYPE html > <html > <head> <meta charset ="ut f - 8" /> <t i t l e>Chapter0 3 </t i t le> <!- - Ссыпки на WinJS - - > < l ink hre f="//Micros of t.WinJS.1.0/c s s/ui - dark.cs s" 4re l ="s tyl esheet" /> <script src="//Microsof t.WinJS.1.0/j s/base.j s"></script> <script src="//Microso f t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссыпки на ChapterO З - - > < l ink hre f ="t emplatesQuery.c s s" rel ="stylesheet" /> <script src ="t emplatesQuery.j s"></s cript> </head> <body> <!- - Шаблон - - > <div id="tmpl Product" data-win-cont rol ="WinJS.Binding.Templat e"> <div cl as s ="product"> Name: <span dat a-win-Ьind="innerText:name"></span> <br /> Price: <span data-win-bind="innerтext:price"></span> Шаблоны </div> </div> <!- - Здесь отрисовывается шабпон - - > <div id="conProduct s"></div> </body> </html> 11• ••1&J На этой странице имеется декларативный шаблон tmplProduct, а также элемент DIV с идентификатором conProducts, внутри кото­
рого отрисовывается шаблон. Влистинге З.2Зпоказано,какспомощьюметодаwinJS. Uti l i ties. id ( ) применить шаблон к элементу conProducts. Листинг 3.2 3. Использование метода WinJS. Utili ties. id ( ) совместно с шаблоном (templatesQuery\templatesQu ery.j s) ( funct i on ( ) { н use stri ct"; funct i on ini t i al i z e ( ) var product s = [ ] ; { name: "Tes l a", price: 3 0 0 0 0 0 } , { name: · в мw ·, price: 8 0 0 0 0 } , { name: "Pinto •, price: 1 0 0 0 0 } WinJS. UI. processAll ( ) . done ( funct ion ( ) { var tmpl Product = document.getElementByid ("tmpl Product"); WinJS.Ut i l i t ies.id ("conProduc t s") .templ at e ( tmpl Product, '+product s ) ; } ) ; document.addEventLi s t ener ("DOMCont entLoaded", ini t i al i z e ); } ) ( ); Отметим, что при использовании метода template ( ) класса QueryCollection вызывать forEach ( ) не нужно - это делает сам ме­
тод template ( ). Внешние шаблоны Если один и тот же шаблон встречается на нескольких страницах, то имеет прямой смысл создать внешний шаблон, то есть поместить шаблон в отдельный файл, а не на ту же страницу, где он использу­
ется. Ш1••11 Глава 3. Наблюдаемые объекты, привязки и шаблоны Ниже показано, как объявить элемент управления Template, ссы­
лающийся на внешний файл шаблона. Обратите внимание на пара­
метр hre f, который указывает на файл productTemplate. html. <!- - Шаблон - - > <div id="tmpl Product" data-win-contro l ="WinJS.Binding.Template" dat a-win-opti ons ="{ hre f: 'productTemplate.html' } "> </div> Файл productTemplate. html содержит шаблон товара: <div cl as s ="produc t"> Name: <span data-win-Ьind="innerтext:name"></span> <br /> Pri ce: < span data-win-bind="innerText:pri ce"></span> </div> Наконец, в листинге 3.24 приведен JavaScгipt-кoд отрисовки внешнего шаблона. Листинг 3.24. Отрисовка внешнего шаблона ( funct i on ( ) { 11 use stri ct"; funct i on ini t ial i z e ( ) var products = [ ] ; { name: "Tes l a", pri ce: 3 0 0 0 0 0 } , { name: "BMW", price: 8 0 0 0 0 } , { name: "Pinto", price: 1 0 0 0 0 } // Получить шаблон и контейнер шаблона var tmpl Product document.getElementByi d ("tmpl Product"); var conProduct s = document.getEl ementByid ("conProduc t s"); /! Отрисовать все элементы массива по шаблону WinJS.UI.processAl l ( ).done ( funct i on ( ) { tmpl Product. winControl. render ( { } ) . then ( funct i on ( ) { produc t s. forEach ( funct i on ( product ) { tmpl Product.winControl.render ( product, conProduct s ); } ) ; } ) ; } ) ; document.addEventLi s tener ("DOMContentLoaded", init i al i z e ); } ) ( ); Резюме На файл productTemplate.html можно ссылаться из элементов управления Template, находящихся на разных страницах. Примечание. В листинге 3.24 методу render ( ) при вызове передается пус­
той объект: tmpl Pr oduct. wi nCont r ol. rend e r ( { } ) . then ( f unct i on ( ) { } ) ; Это сделано для того, чтобы обойти известную ошибку в библиотеке Wi n­
JS. Р ез юме Эта глава была посвящена отображению JаvаSсгiрt-объектов на странице. В первом разделе вы узнали о наблюдаемых объектах и, в частности, о том, как они позволяют автоматически обнаружи­
вать факт изменения свойства jаvаSсгiрt-объекта. Мы рассмотрели также вопрос об использовании объекта WinJS. Binding. Li s t для обнаружения разного рода изменений, происходящих с массивом элементов. Затем вы узнали о применении декларативной привязки к дан­
ным для отображения на странице значений свойств JаvаSсгiрt-объ­
ектов - обычных и наблюдаемых. Наконец, я рассказал о том, как использовать шаблоны WinJS для форматирования и отображения массивов объектов. Вы научились создавать шаблоны императивно и декларативно. rЯАВА 4. Эnеме нты уnр ав nе ния Wi nJ S В этой главе: • Введение в элементы управления WinJS.
• Элемент управления Tooltip.
• Элемент управления ToggleSwitch.
• Элемент управления Rating.
• Элемент управления DatePickeг.
• Элемент управления TimePickeг.
• Элемент управления FlipView.
Цель этой главы - дать обзор элементов управления, включенных в библиотеку WinJS. Мы начнем с вопроса о том, как поместить эле­
мент управления на страницу. Вы узнаете, как создавать элементы управления императивно и декларативно и как задавать их свойства. Большая часть главы посвящена описаниям и примерам исполь­
зования простейших элементов управления. Ниже мы рассмотрим следующие элементы: Tool tip - выводит всплывающую подсказку; ToggleSwi tch - отображает переключатель, применяемый в тех же случаях, что флажок; Rating - показывает рейтинг и позволяет взаимодействовать с ним; DatePicker - ввод даты; TimePicker - ввод времени; FlipView - отображение подробных сведений об одном элементе коллекции. Введение в элементы управления Wi nJS В ведение в э ле мент ы управл ения WinJ S Для объявления элемента управления WinJS используется атрибут data-win-control. Например, чтобы объявить элемент DatePicker, нужно поместить на страницу такой DIV: <div id="dateBirthday" data-win-control="WinJS.UI.DatePicker"></div> Сам по себе этот элемент DIV ничего не делает, а лишь предостав­
ляет место для элемента управления. Можно сказать, что это <�владе­
лец�> элемента управления DatePicker. С помощью атрибута data­
win-control указывается, какой элемент управления ассоциировать с DIV'oм. Элемент DIV не превратится в элемент управления, пока не бу­
дет вызван метод WinJS. UI. proces sAll ( ). Этот метод разбирает НТМL-документ, находит в нем все элементы с атрибутом data-win­
control и порождает из них элементы управления. Для работы с элементами управления WinJS нужно обязательно включить ссылки на соответствующие JavaScгipt и СSS-файлы, то есть поместить в начало НТМL-страницы такие три строчки: < l ink hre f="//Micros of t.WinJS.1.0/c s s/ui -dark.c s s" rel="styl esheet" /> <s cript src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <script src="//Micros of t.WinJS.1.0/j s/ui.j s"></script > Первая строка ссылается на файл ui-dark. cs s, содержащий каскадную таблицу стилей. В состав библиотеки WinJS включены две таблицы стилей: ui -dark. c s s и ui - l ight. cs s. Если вместо ui­
da r k . с s s указать u i - 1 i g h t . с s s, то для отрисовки всех элементов управления будет использована светлая тема. На рис. 4.1 показано, как выглядит элемент DatePicker, когда вы­
брана темная тема, а на рис. 4.2 - он же в светлой теме. В следующих двух строчках находятся ссылки наj аvаSсгiрt-файлы base.j s и ui.js. Для работы с элементами управления WinJS нужны оба эти файла. Исходный код всех элементов управления WinJS, в частности DatePicker и Fl ipView, находится в файле ui.js. Чтобы просмотреть содержимое любого из этих файлов, раскройте папку Refeгences ( Ссылки) в обозревателе решения Visual Studio (рис. 4.3) и дважды щелкните по имени файла. Глава 4. Элементы управления WiпJS ' � т0 • ;t [с�· 8i1 !® _. i;':J s�м:::!1 Sc!ut1c11 Елр!Оf(!:Г (Ctri ... ;) р ... tijj Solution 'Controls' (1 project) � Controls Рис. 4. 1. Темная тема Рис. 4.2. Светлая тема Рис. 4.3. Просмотр файлов Wi nJS в обозревателе решения Декларативное создание элемента управления WinJS Создать элемент управления WinJS можно двумя способами: декла­
ративно и императивно. В листинге 4.1 показана НТМL-страница с декларативно созданным элементом. Листинг 4. 1 • Декларативное создание элемента управления Wi пJS ( declarative \ declarative. html ) <!DOCTYPE html > <html > <head> Введение в элементы управления Wi nJS <met a charset ="ut f - 8" /> <t i t le>Chapter0 4</t i t le> <!- - Ссылки на WinJS - - > •••••ШJ < l ink hre f ="//Microsof t.WinJS.1.0/c s s/ui -dark.c s s • '+rel = • stylesheet • /> < script src="//Microsof t.WinJS.1.0/j s/base.j s"></script> <script src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 4 - - > < l ink hre f ="/c s s/def aul t.c s s • rel ="s tyleshee t" /> <script src="decl arat ive.j s"></s cript> </head> <body> <div i d="dateBirthday• </body> </html> data-win-cont rol ="WinJS.UI.Dat ePicker"></div> Здесь в теле НТМL-страницы объявлен элемент управления Da tePicker. Отметим, что страница включает ссылки на все три фай­
ла - ui-daгk.css, base.js, и ui.js, - необходимые для работы с элемента­
ми управления WinJS. Имеется также ссылка на файл declaгative.js, который содержит наш собственный код, относящийся к этой странице. Находящийся в нем код приведен в листинге 4.2 . Ли сти нг 4.2. Декларативное создание элемента управления Wi nJS ( declarative\declarative.js) ( func t ion ( ) { "use stri ct"; funct ion init i al i z e ( ) WinJS.UI.processAl l ( ); document.addEvent Li s t ener ("DOMCont entLoaded", ini t i al i z e ); } ) ( ); В листинге 4.2 присутствует обращение к методу WinJS. ur. proces sAl l ( ).Если вы забудете вызвать его (а это очень просто), то объявленные на странице элементы никогда не станут элементами управления. Обратите внимание, что вызов WinJS. ur. proces sAll ( ) произво­
дится из функции ini ti ali z e ( ),которая вызывается не раньше, чем произойдет событие DOMContentLoaded, то есть после загрузки всего НТМL-документа. Необходимо дождаться завершения загрузки до-
DD••ll Глава 4. Элементы управления WiпJS кумента, иначе методу WinJS. UI .proces sAll ( ) будет просто нечего обрабатывать. Императивное создание элемента управления WinJS В предыдущем разделе мы создали экземпляр элемента управления DatePi cker декларативно, с помощью НТМL-разметки. Альтернативой является императивное создание элементов, то есть целиком в коде нa javaScгipt. Взгляните на страницу в листинге 4.3. На ней присутствует элемент DIV с идентификатором dateBi rthday. У этого элемента нет атрибута data-win-control. Листинг 4.3. Им пера ти вное созд ание элеме нта управл ени я WinJS ( controllmperative \ controllmperative. html ) <!DOCTYPE html> <html > <head> cmeta charset ="ut f - 8" /> <t i t le>Chapter0 4 </t i t le> <!- - Ссылки на WinJS - - > < l ink href ="//Micros of t.WinJS.1.0/c s s/ui -dark.cs s • 4re l ="stylesheet • /> <script src="//Microsof t.WinJS.1.0/j s/base.j s"></script> < script src="//Microsof t.WinJS.1.0/j s/ui.j s • ></script > <!- - Ссылки на Chapter0 4 - - > < l ink hre f="/cs s/def aul t.c s s • rel ="stylesheet • /> <s cript src = • control imperat ive.j s • >c/script> </head> <body> <div i d="dateBirthday"></div> </body> </html > Код в листинге 4.4 создает элемент управления DatePicker и ас­
социирует его с указанным элементом D IV. Листинг 4.4. Им пе рати вно е создан ие эле ме нта управления WinJS (controllmperative\controllmperative.js) ( funct ion ( ) { • use stri ct"; funct ion ini t i al i z e ( ) Введение в элементы управления Wi nJS ••• ••ШJ var dat eBirthday = document.getEl ementByid ("dateBirthday"); var ct lBirthday = new WinJS.UI.Dat ePi cker ( dateBirthday ); document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Здесь мы создаем новый экземпляр JavaScгipt-клacca WinJS. UI. DatePicker ( ),передавая идентификатор dateBi rthday элемента DIV конструктору класса DatePicker. Отметим, что вызывать метод WinJS. UI. proce s sAl l ( ) не нужно, потому что при императивном создании элементов управления документ не разбирается. В листинге 4.4 подчеркнут тот факт, что элемент управления WinJS - на самом деле всего лишь класс JavaScгipt. Экземпляр этого класса можно создать декларативно, задав атрибут data-win­
control, или императивно - в программе. Но в любом случае мы получаем экземпляр класса. В этой книге я пользуюсь декларативным подходом. Они ничем не лучше императивного, но коль скоро Майкрософт в примерах отдает предпочтение именно декларативному стилю, то и я поступаю так же. Задание параметров элемента управления У большинства элементов управления есть параметры. Например, при создании элемента TimePicker можно указать время по умолча­
нию или формат представления времени ( 1 2 или 24-часовой). Параметры элемента можно задавать декларативно - в атрибуте data-win-options. Например, в следующем фрагменте показано, как задать текущее время (3:04pm) и 24-часовой формат для элемента управления TimePicker: cdiv i d="t imeLunch" data-win-control ="WinJS.UI.TimePi cker" data-win-opt i ons ="{ current: '3: 0 4 pm' , cl ock: '2 4 HourClock' }"></ div > Параметры передаются элементу управления в виде объекта JavaScгipt - потому-то имена и значения и заключены в фигурные скобки. При желании параметры можно задавать императивно. Ниже по­
казано, как программно создать экземпляр элемента TimePi cker и задать для него текущее время и формат представления: ( funct i on ( 1 { "use strict"; funct i on ini t i al i z e ( } Глава 4. Элементы управления WinJS var divLunch = document.getElementByid ("t imeLunch"}; var ctrlLunch = new WinJS.UI.TimePicker ( t imeLunch, { current: '3: 0 4pm' , clock: '2 4HourClock' } } ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } } ( }; Объект параметров передается конструктору TimePi cker во вто­
ром аргументе. Примечание. Для императивного задания параметров элемента управле­
ния можно воспользоваться также методом Wi nJ S. UI. set O pt i ons ( }. Этот метод следует вызывать после того, как объект сконструирован. Извлечение элементов управления из НТМL-документа Всякий элемент управления ассоциируется с некоторым элемен­
том DOM - «владельцем». Получить элемент управления из ассо­
циированного с ним элемента DOM можно с помощью свойства winControl. Например, если объявить элемент управления DatePicker следу­
ющим образом: <div id="dateBi rthday" data-win-control ="WinJS.UI.Dat ePicker"></div> то получить его в JavaScгipt-кoдe можно будет так: ( funct ion ( 1 { "use stri ct"; funct i on ini t ial i z e ( ) WinJS.UI.proces sAl l ( ) .done ( func t i on ( ) { var ct lBirthday = document.getEl ementByi d ("dateBirthday"). '-+-winControl; ctl Birthday.current = "1 2/2 5/1 9 6 6"; } ) ; document.addEventLi s t ener ("DOMContentLoaded", ini t ial i z e ); } ) ( ); Элемент управления Tool ti p �· · ••ШJ Метод document. getElementByid ( ) возвращает элемент DOM, а не элемент управления. Но, получив элемент DOM, мы можем с помощью свойства winControl добраться до ассоциированного с ним элемента управления WinJS. Метод WinJS. ur. proces sAll ( ) возвращает обещание. Перед тем как пытаться взаимодействовать с элементами управления, необхо­
димо дождаться завершения разбора документа и создания декла­
ративных элементов. В показанном выше коде элемент DatePicker извлекается после исполнения обещания, возвращенного методом proces sAl l ( ). Элемен т управления Tooltip Элемент WinJS Tool tip можно использовать для отображения настраиваемой всплывающей подсказки над любым элементом HTML (рис. 4.4). Подсказка появляется на несколько секунд, если задержать курсор мыши над элементом. Как только курсор покидает элемент, подсказка исчезает. Рис. 4.4. Отображе ние всплывающей подсказки с помощью элемента управления Tooltip Элемент Tool tip помещается на страницу следующим образом: <button id="ЬtnDel ete" data-win-control ="WinJS.UI.Tool ti p" data-win-opt ions ="{ innerHTML: 'Deletes the <b>record</b> from the dat abase' }">Delete</but t on> Предупреждение. Не забывайте вызывать методwinJS. UI .pr oces sAl l ( ), иначе элемент Tool t i p, как и любой другой элемент управления Wi nJS, не появится. Отметим, что для задания текста подсказки служит параметр innerHTML, причем текст может содержать НТМL-теги, например В или I МG. Глава 4. Элементы управления WinJS Использование свойства contentE/ement Если подсказка содержит много НТМL-кода (рис. 4.5), то можно по­
местить этот код в отдельный элемент, как показано ниже: <button id="btnDel ete" data-win-control ="WinJS.UI.Tool t ip" dat a-win-opt i ons ="{ cont entElement: select ('#btnDel eteTool t ip') }">Delete</ button > <div style =" display : none"> <div id="btnDeleteTool t ip"> Deletes the <b>record</b> from the databas e. D o you <i >real ly, real ly</i > want to do thi s? The record wi l l Ье gone forever and you might weep. </div> </div> Для задания отдельного элемента, в котором находится НТМL­
содержимое подсказки, используется параметр contentElement. В примере выше содержимое находится в элементе DIV с идентифи­
катором ЬtnDeleteTool tip. Отметим, что этот DIV погружен в другой, для которого задан ат­
рибут style="di splay:none". Внешний DIV предотвращает показ текста подсказки на странице. Мы хотим, чтобы этот текст отобра­
жался только внутри самой подсказки, когда она появляется. Flux Capac1tor State ЕпаЫеd �-\ Рис. 4.5. Дл инная всплывающая подсказка Стилизация всплывающей подсказки В стандарте HTML уже определен атрибут для показа всплывающей подсказки, так зачем же Майкрософт ввела еще и элемент управления WinJS Tooltip? Всё дело в настройках. Элемент Tooltip позволяет создавать подсказки, согласованные со стилем вашего приложения Магазина Windows. Для настройки внешнего вида подсказки можно воспользовать­
ся СSS-классом win-tooltip. На рис. 4.6 показано, как выглядит элемент Tooltip, если определить следующий стиль win-tooltip: .win-tool t ip { background-color: #f fd8 0 0; Элемент управления Toggl eSwitch border: s ol i d 2рх red; border- radius: 1 5рх; Flux Сарас1tаг State DisaЫed р Рис. 4.6. Настройка внешнего вида всплывающей подсказки ••••ШJ Элеме нт управления ToggleS wi tc h Элемент управления WinJS ToggleSwitch полезен в тех же случа­
ях, что и стандартный флажок HTML ( <input type="checkbox"> ). Разница между ними в том, что ToggleSwitch лучше приспособлен к работе пальцами: движение пальцем поперек элемента изменяет его состояние (рис. 4.7 и 4.8). Flux Сарас1tаг State EnaЫed �� Flux Сарас1tог actJvatedl Рис. 4. 7. Отмеченный элемент ToggleSwitch Рис. 4.8. Неотмеченный элемент ToggleSwitch Элемент ToggleSwi tch помещается на страницу следующим об­
разом: <div dat a-win-control ="WinJS.UI.Toggl eSwitch" dat a-win-opti ons ="{ t i t l e: 'Flux Capac i t or State', l abelOf f: 'Di saЫed', l abelOn: 'EnaЫed', checked: true }"></div> Глава 4. Элементы управления WinJS Определение состояния ToggleSwitch Для того чтобы определить, отмечен элемент ToggleSwitch или нет, следует опросить его свойство checked. Так, страница из листинга 4.5 содержит элемент управления ToggleSwi tch и элемент DIV, в кото­
ром отображаются различные сообщения в зависимости от состояния ToggleSwi tch(pиc. 4.9). * * � • Рис. 4.9. Отображение сообщения, зависящего от сост ояния ToggleSwitch Лис ти нг 4.5. Использование элемента управления ToggleSwitch (toggleSwitchCh ecked\toggleSw itchChec ked.html ) <div id ="togFlux• dat a-win-control ="WinJS.UI.ToggleSwi tch" dat a-win-opti ons ="{ t i t l e: 'Flux Capac itor State', l abelOf f: 'Di saЫed', l abelOn: 'EnaЫed', checked: true }"></ div > <div id="divMes s age"></div> В листинге 4.6 к элементу ToggleSwi tch присоединяется обработ­
чик события. Он вызывается всякий раз при изменении состояния элемента и выводит в DIV одно из двух сообщений. Ли сти нг 4.6. V,1спользование элемента управления ToggleSwitch (toggleSwitchChec ked\toggleSw itchChecked.js) ( functi on ( ) { 11 use stri ct"; funct i on ini t i al i z e ( ) WinJS.UI.proces sAl l ( ).done ( funct i on ( ) { var togFlux = document.getElementByid ("togFlux") .winControl; var divMes s age = document.getElementByid ("divMes s age"); t ogFlux.addEventLi s tener ("change •, funct ion ( evt ) ( i f ( togFlux.checked) ( divMes s age.innerHTML } el s e ( divMes s age.innerHTML } ; "Flux Capac i t or act ivated!" "F lux Capac i t or de-act ivated." Элемент управления Rati ng } ) } ) ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ) ; Не забудьте вызвать метод proce s sAl 1 ( ) , перед тем как обращать­
ся к элементу ToggleSwi tch и изменять его свойства (в том числе при­
соединять обработчик события). Чтобы с элементом ToggleSwi tch можно было что-то сделать, он должен существовать! Элеме нт управле ния Rati ng Элемент WinJS Rating служит для ввода и отображения поставлен­
ных пользователями оценок. По умолчанию этот элемент позволяет выставить от одной до пяти звездочек (рис. 4.1 0). Поставить оценку можно мышью, пальцем или с помощью клавиш со стрелками вверх­
вниз или вправо-влево (когда элемент находится в фокусе). Рис. 4.1 О. Ввод оценки Вот как объявляется элемент управления Rating: cdiv id="rat ingProduct" data-win-control ="WinJS.UI.Rat ing"></div> При объявлении можно задать два параметра: оценку по умолча­
нию ( averageRating) и признак, показывающий, может ли пользо­
ватель стереть оценку (по умолчанию true). cdiv i d="rat ingProduct" data-win-contro l ="WinJS.UI.Rat ing" data-win-opt ions="{ averageRat ing:З, enaЬleCl ear:fal s e } "></div> Чтобы стереть оценку, нужно провести пальцем по элементу справа налево. В результате не останется ни одной отмеченной звездочки. Глава 4. Элементы управления WinJS Предупреждение. Не забывайте вызывать метод Wi nJ S. UI. pr oces sAl l ( ) , иначе элемент Rating не появится. Настройка элемента Rating Можно задать количество отображаемых звездочек и подсказ­
ки, всплывающие для каждой оценки. Для этого служат свойства maxRating и tooltipStrings. Например, ниже описан элемент Rating с тремя звездочками и подсказками «bad», «okay» и «gгeat!» (рис. 4.1 1 ). <div id="rat ingProduct" data-win-control ="WinJS.UI.Rat ing" dat a-win- opt i ons="{ averageRat ing:2, maxRat ing:З, toolt ipStrings: ['bad', 'okay', 'great!'] }"></div> Рис. 4.1 1. Настройка элемента Rating Обратите внимание, что значением свойства tool tipStrings должен быть массив] avaScгi pt. Каждыйэлементмассивасоответствует количеству звездочек, начиная с единицы. Отправка оценки Элемент управления Ra ting генерирует три события: previewchange, cancel и change. Событие previewchange возникает, когда пользо­
ватель задерживает курсор мыши над звездочкой, событие cancel -
если после задержки курсора оценка так и не выбрана, а событие change - в результате щелчка по звездочке. Элемент управления Rati ng Страница в листинге 4.7 содержит элемент управления Rating и элемент DIV. Когда вы изменяете оценку товара или просто подумы­
ваете о том, чтобы ее изменить, в элементе DIV появляется сообще­
ние. Рис. 4.1 2. Обработка событий previewchange, cancel и change элемента Rating Листинг 4. 7. Обработка событий элемента управления Ra ting ( ratingSubmit\ratingSubmit.html ) <hl >Rat e our Store!</hl> <div i d="rat ingStore • data-win-control ="WinJS.UI.Rat ing• data-win-opt i ons="{ maxRat ing: 3 }"></div> <div id="divMes s age"></div> В листинге 4.8 приведен код обработчиков событий preview­
change, cancel и change элемента Rating. Листинг 4.8. Обработка событий элемента управления Ra ting ( ratingSubmit\rat ingSubmit.js) ( funct i on ( ) { • use stri ct"; funct i on ini t i al i z e ( ) WinJS.UI.proces sAl l ( ).done ( funct i on ( ) { Ш1••111 Глава 4. Элементы управления WinJS var rat ingS tore document.getEl e mentByi d ("r at ingStore") .winContr ol; var divMessage = do cu ment.ge t E l e mentB y i d ("d i v Mes s a ge"); rat i n gSt ore.addEve nt Li st e ner ("previ ewchange", fun ct i on ( evt ) var t entat iveRat ing = evt.detai l.tentat iveRat ing; swi tch ( tentat iveRat ing ) { } } ) ; case 1: divMessage.innerHTML break; case 2: div Messa ge.i nne rHTML break; case 3: divMes s age.innerHTML break; "Don't do i t! That's j ust mean!"; "Okay, you sure? We'l l t r y harder!"; "Thanks! "; rat ingSt ore.addEvent Li s t ener ("cancel", funct i on ( evt ) divMes s age.innerHTML = ""; } ) ; ratingStore.addEventLi s t ener ("change", funct i on ( evt ) { var us erRat ing = rat ingSt ore.us erRat ing; swi tch ( userRat ing ) { } } ) ; } ) ; c as e 1: divMessage.innerHTML break; case 2: divMes s age.innerHTML break; c a s e 3: divMes s age.innerHTML break; "You gave us t h e wors t rat ing."; "You gave us an okay rat ing."; "You �ave us а good rat ing."; document.addEventLi s tener ("DOMContentLoaded", ini t i al i ze ); } ) ( ); При наведении курсора на разные звездочки обработчик события previewchange выводит различные сообщения. После щелчка по звездочке генерируется событие change, и его обработчик выводит окончательное сообщение. Эле ме нт уп равл е ния DatePick er Элемент управления Wi11JS DatePicker, как нетрудно догадаться, позволяет выбрать дату. Он показывает три списка: месяц, день и год (рис. 4.1 3). Элемент управления DatePi cker 111••Ш1 J Об - June 16 Sat v l 201 2 д.о. v 1 Рис. 4.1 3. Элемент управления DatePicker Объявляется элемент DatePicker следующим образом: <div i d="dateBirthday" data-win-control ="WinJS.UI.DatePi cker"></div> Предупреждение. Не забывайте вызывать метод WinJS. ur. proces sAll ( ) , иначе элемент DatePi cker не появится. По умолчанию в элементе DatePicker выбрана текущая дата. Но можно задать любую другую с помощью свойства current: <div i d="dateBirthday" data-win-control ="WinJS.UI.Dat ePi cker" dat a-win-opti ons ="{ current: '1 2/2 5/1 9 6 6' }"></ div > В результате в элементе DatePi cker будет выбрана дата '1 2/25/1 966'. Форматирование даты В свойства yearPattern, monthPattern и datePattern можно запи­
сать форматные строки, управляющие представлением года, месяца и дня соответственно. Форматная строка может содержать любые сим­
волы и обычно в ней указывается один или несколько спецификато­
ров формата даты. Специфи ка тор ф орма т а - это специальная строка, задающая формат вывода даты. В строке yearPattern могут встречаться следующие специфика-
торы формата: {yeaг.full} {yeaг.ful l ( n)} {yeaг.abbгeviated} {yeaг.abbгeviated ( n)} { eгa.abbгeviated} { eгa.abbгeviated( n)} В строке monthPattern могут встречаться следующие специфика­
торы формата: { month.full} { month.abbгeviated} { month.abbгeviated( n)} { mon th.solo.full} { month.solo.abbI"eviated} {month.solo.abbгeviated(n)} • {month.integeг} { month.in tegeг( n)} Глава 4. Элементы управления WinJS Наконец, в строке datePattern могут встреqаться следующие спецификаторы формата: { day.integeг} {day.integeг (n) } { dayofweek.full} { dayofweek.abbeviated} { dayofweek.abbгeviated( n)} { dayofweek.solo.full} { dayofweek.solo.abbeviated} • { dayofweek.solo.abbгeviated( n)} Здесь (п) -число. Например, чтобы вывести день месяца с двумя цифрами (первая может быть нулем), нужно использовать специфи­
катор формата { day.integeг(2)}. Бот как можно вывести дату в виде года, месяца и дня, представ­
ленных <rислами: <div id="dateBirthday" data-win-control ="WinJS.UI.DatePicker" data-win-opt i ons ="{ monthPatt ern: '{ mont h.integer ( 2 ) }', dat ePat tern: ' { day. int eger ( 2 ) } ' , yearPat t ern: ' { year. abbreviated} ' }"></div> Поскольку для дня (datePattern) задан спецификатор {day. integeг(2)}, то даты первых девяти дней месяца выводятся с начальным нулем (рис. 4.1 4). Рис. 4.1 4. Фор матирование года, месяца и дня В одной форматной строке может встречаться несколько специ­
фикаторов формата. Например, если требуется показать не только день месяца, но и день недели, и не только сам год, но еще и эру, то можно объявить элемент DatePicker следую1.цим образом: Элемент управления DatePi cker <div i d="dateBirthday" data-win-contro l ="WinJS.UI.Dat ePi cker" data-win-opt i ons="{ monthPattern: '{ month.integer ( 2 ) } - { month.ful l }', datePat t ern: '{ day.integer ( 2 ) } { dayofweek.abbreviated}', yearPat tern: '{year.ful l } { era.abbreviated}' }"></div> Обратите внимание, что в форматной строке monthPattern встре­
чается знак дефиса ( - ). Вообще, в ней могут быть любые символы, а не только спецификаторы формата (рис. 4.1 5). B1rthday _ _ _ Vour b1rthday 1s on Sun Dec 25 1966 Рис. 4.1 5. Комбинирование форматных строк Примечание. На внутреннем уровне элемент DatePicker пользуется для форматирования дат классом Wi nRT Windows. Globali z ation. DateTime­
Formatter и применяемыми в нем шаблонными строками. Показ только годов, месяцев или дней Иногда нужно, чтобы пользователь мог выбрать только месяц, но не день и не год. Например, если вы создаете службу заказа частных са­
молетов, в которой клиентам разрешается размещать заказ только на целый месяц. В листинге 4.9 показано, как скрыть годы и дни, оставив в элемен­
те DatePicker только месяцы. Ли сти нг 4. 9. Показ только месяцев ( datePi ckerMonthOnly\datePickerMonthOn ly. html ) <!DOCTYPE html> <html > <head> <meta charset ="ut f - 8" /> <t i t l e>Chapter0 4 </t i t l e> <!- - Ссылки на WinJS - - > < l ink hre f="//Microsof t.WinJS.l.0/c s s/ui-dark.cs s" �rel ="s tyl esheet" /> <script src="//Micros of t.WinJS.1.0/j s/base.j s"></s cript> <script src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 4 - - > Глава 4. Элементы управления WinJS <l iпk hre f ="/c s s/defaul t.c s s • rel ="s tylesheet" /> <script src ="datePickerMoпthOпly.j s"></script> <s tyle type="text/c s s"> #dateBirthday .wiп-datepi cker-date di splay: попе ; #dat eBirthday .wiп-dat epicker-year di splay: попе ; } </s tyle> </head> <body> <div id="dateBirthday• data-wiп-coпtrol ="WiпJS.UI.DatePicker • data-wiп-opt i oпs="{ moпthPat t erп: ' { moпth. s ol o. ful l } ' }"></div> </body> </html> На странице из листинга 4.9 имеются два правила CSS, которые скрывают списки дней и годов в элементе управления DatePicker (рис. 4.1 6). Рис. 4. 1 6. Показ только списка месяцев Получение выбранной даты Элемент DatePicker генерирует одно и то же событие change при изменении любой компоненты даты: дня, месяца или года. Для полу­
чения выбранной даты нужно обработать это событие. Например, на странице из листинга 4.1 О находится элемент управления Da te Р i с ke r и элемент DIV, в котором отображается выбранная дата (рис 4.1 7). Рис. 4.1 7. Отображение даты, выбранной в элементе DatePicker Элемент управления Ti mePi cker Ли стин г 4. 1 О. Получение выбранной даты ( datePicker Change\datePickerChange .html ) <l abel>Birthday:</l abel > <div i d="dateBirthday" data-win-control ="WinJS.UI.Dat ePi cker"></div> <div i d="divMes s age"></div> А в листинге 4.1 1 приведен JavaScгipt-кoд обработки события change, в котором мы получаем выбранную дату. Листинг 4.1 1. Получение выбранной даты ( datePicker Change\datePickerChange .js) ( funct ion ( ) { "use stri ct"; funct i on ini t i al i z e ( ) WinJS.UI.processAl l ( ) .done ( funct i on ( ) { var dateBirthday = document.getElementByi d ("dateBirthday"). <+winControl; var divMes s age = document.getElementByid ("divMes s age"); dateBirthday.addEvent Li s t ener ("change", funct i on ( evt ) { divMes s age.innerHTML = "Your Ьirthday i s оп " } ) ; } ) ; + dateBirthday.current.t oDateString ( ); document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Элеме нт управле ния Ti me Pick er Элемент управления WinJ S т ime Р i с k e r позволяет выбрать время. По умолчанию TimePi cker выводит три списка для выбора часа, минуты и времени суток (АМ/РМ - до или после полудня). Select a luncl1 T1me - - -
Lunch t1me " 12 30 00 РОТ Рис. 4. 1 8. Элемент управления TimePicker Элемент управления TimePi cker объявляется следующим обра­
зом: <div i d="t imeLunch" data-win-control ="WinJS.UI.TimePicker"></div> 1Ш•• 1 11 Глава 4. Элементы управления WinJS Не забудьте вызвать метод WinJS. ur. proces sAl l ( ),иначе объяв­
ление элемента TimePicker не будет разобрано и не превратится в элемент управления. Если вы предпочитаете 24-часовой формат, то можете задать свойство clock: cdiv id="t imeLunch" data-win-control ="WinJS.UI.TimePi cker• dat a-win-opt i ons="{ c lock: '2 4 Hourclock' } "></div> Наконец, чтобы показывать минуты с шагом 15 минут (рис. 4.1 9), а не с шагом 1, как подразумевается по умолчанию, задайте свойство minuteincrement: cdiv id="t imeLunch" data-win-control ="WinJS.UI.TimePi cker• data-win-opti ons ="{ minuteincrement: 1 5 }"></div> Рис. 4. 1 9. Изменение шага приращения минут в элементе TimePi cker Примечание. Если в региональных настройках компьютера установлен 24-часовой, а не 1 2-часовой формат времени, то TimePicker по умолчанию будет настроен на 24-часовой формат. Элемент управления Ti mePi cker Получение и установка текущего времени По умолчанию в элементе TimePicker отображается текущее вре­
мя. Но это можно изменить с помощью свойства current, которое используется как для чтения, так и для установки времени. Напри­
мер, на странице из листинга 4.1 2 находится элемент управления TimePicker и элемент DIV, в котором отображается сообщение (рис. 4.20). Элемент TimePicker объявлен так, что в нем отобража­
ется время 1 2:00pm. Рис. 4.20. Отображе ние времени обеденного перерыва в элементе управления Ti mePicker Листинг 4. 1 2. Установка текущего времени в элементе управления Time Picker (timePickerSet\timePickerSe t.html ) <l abel >Select а Lunch Time:</l abel > <div id="t imeLunch" data-win- control="WinJS.UI.TimePicker" data-win-opt i ons ="{ current: '1 2:0 0pm' }"></div> <div id="divMes s age"></div> В листинге 4.1 3 показано, как получить время, выбранное в эле­
менте TimePicker. При выборе времени в элементе DIV отображает­
ся сообщение. Листинг 4. 1 З. Установка текущего времени в элементе управления Time Picker (timePickerS et\timePickerSe t.html ) ( func t i on ( ) { "use stri ct"; funct ion ini t ial i z e ( ) WinJS. UI. processAll ( ) . done ( funct i on ( ) { 1Ш••111 Глава 4. Элементы управления WinJS var t i me Lunch = docu ment.get El emen t B y i d ("t i m eLun ch"I .wi n Con t r ol; var divMes sage = document.getElementByid ("divMes s age"J; t imeLunch.addEvent Li s t ener ("change", funct i on ( evt ) { divMes s age.innerHTML = "Lunch t ime i s " + t imeLunch.current.t oTimeString ( ); } 1 ; } ) ; document.addEventLi s tener ("DOMCont entLoaded", ini t i al i z e ); } ) ( ); Поскольку в языке jаvаSсгiрt нет отдельного типа Time, свойство current элемента TimePicker возвращает текущую дату и выбран­
ное время. В листинге 4.1 2 из полученного значения с помощью стан­
дартной функции toTimeString ( ) выделяется время. Примечание. Согласно википедии ( http://en.wi ki pedi a.org/wi ki/Noon), пол­
день - это не 1 2:00am и не 1 2:00pm, а просто « 1 2 noon». Странно. Форматирование часа, минуты и времени суток Для изменения внешнего вида данных в списках часов, минут и вре­
мени суток можно использовать шаблонные строки. Такая строка может содержать произвольные символы и в том числе специальные спецификаторы формата для различных компонентов времени. В свойстве hourPattern можно использовать следующие специ­
фикаторы формата: {houг.integer} { houг.integeг(n)} В свойстве minute Pattern можно использовать следующие спе­
цификаторы формата: {minute.integeг} { minute.integeг(n)} В свойстве periodPattern можно использовать следующие спе­
цификаторы формата: {peгiod.abbгeviated} {peгiod.abbгeviated( n)} Здесь (п) - число. Например, спецификатор lюuг.integeг( 2) при­
ведет к появлению начального нуля при отображении часа с одной цифрой. Элемент управления Fl i p View В объявлении элемента Time Picker ниже показано, как настро­
ить внешний вид списков для выбора часа, минуты и времени суток (рис. 4.2 1 ). <div i d="t imeLunch" data-win-control ="WinJS.UI.TimePi cker" data-win-opt i ons="{ hourPattern: 'hour: { hour.integer ( 2 ) }', minutePat t ern: 'minut e: { minut e. integer ( 2 ) } ' , peri odPat t ern: 'period: ( period.abbreviated}' }"></div> Рис. 4.21. Форма тирование часа, минуты и времени суток Элеме нт управле ния Fl ipVi ew Элемент Fl ipView можно использовать для отображения коллекции объектов. В отличие от элемента Li s tView (который будет рассмот­
рен в главе 7), элемент Fl ipView в каждый момент отображает только один объект. Элемент Fl ipView идеален для просмотра фотогалерей - по одной фотографии. Он годится также для перелистывания перечня жур­
нальных или газетных статей. В элементе Fl ipView одновременно отображается только один объект из источника данных. Однако с помощью стрелок можно пере­
ходить к предыдущему или следующему объекту. Так, в листинге 4.1 4 приведена коллекция, состоящая из трех статей. У каждой статьи есть заголовок, автор и текст ( articleText). Листинг 4.1 4. Сп исок статей ( funct ion ( ) { "use s tri ct"; // Создаем список статей var l i s tArt i cl es = new WinJS.Binding.Li s t ( [ ВD•• l l l Глава 4. Элементы управления WinJS t i t l e: "Why Dogs are Better than Cat s" , author: "Arnol d Wiggl es" , art i c l e Text: "P el l e nt esq ue habi t a nt morbi t r i s t i q ue senect us et \ netus et mal esuada f ames ас turpi s eges t as. Proin pharetra '+nonummy pede. Mauri s et orci." } ' { t i t l e: "Why Dogs are Better than Fish" , author: "Jane RubЬl e" , ar t i cl e Text: "L orem ipsu m do l or s i t amet, cons ect etuer adi pi sci ng \ el i t. Maecenas port t i tor congue massa. Fus ce posuere, magna sed pulvin ar \ ul t ri ci es, purus l ectus mal es uada l i bero, s i t amet commodo magna \ eros qui s urna." } ' { t i t l e: "Why Dogs are Better than Mi ce" , author: "Eric Alexander" , ar t i cl e Text: "L orem ipsum do l or s i t amet, cons ect e tuer adi pi sci ng \ el i t. Maecenas port t i tor congue mas sa. Fus ce posuere, magna sed pulvinar \ ul t ri ci es, purus l ectus mal esu ada l i bero, s i t amet commodo magna \ eros qui s urna." } ] ) ; WinJS .Namespace. def ine ( "MyDat a" { l i s tArt i c l es: l i s tArt i cl es } ) ; } ) ( ); Пусть теперь требуется показать эти статьи по одной в приложе­
нии Магазина Wiпdows. На примере страницы из листинга 4.1 5 по­
ка зано, как мuжнu вuспuльзuваться элементом управления Fl ipView для показа отдельных статей из списка (рис. 4.22). Обратите внимание, что в листинге 4.1 5 имеется ссылка на файл aгticles.js. Это данные для элемента FlipView, которые как раз и при­
ведены в листинге 4.1 4. Рис. 4.22. Отображе ние стат ьи в элементе Fl i pView Элемент управления Fl i pView Ли сти нг 4.1 5. Отображение статьи в элементе Fli pView (flipView\flipView.html) <!DOCTYPE html > <html > <head> <meta charset ="ut f - 8" /> <t i t le>Chapter0 4 </t i t l e> <!- - Ссылки н а WinJS - - > < l ink hre f ="//Microsof t.WinJS.1.0/c s s/ui -dark.c s s • 4re l ="s tyl esheet • /> <s cript src="//Micros of t.WinJS.1.0/j s/bas e.j s"></script> <script src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Chapter0 4 - - > < l ink rel ="s tyl esheet • type ="t ext/c s s • hre f ="f l ipView.c s s • /> <s cript src ="arti cl es.j s"></script> <script src="f l ipVi ew.j s"></s cript> </head> <body> <div id="tmplArt i c l e" dat a-win-control ="WinJS.Binding.Template"> <div c l as s ="art i cl ei t em"> <h2 data-win-Ьind="innerText:t i t l e"></h2 > Author: < span data-win-bind="innerText:author"></span> <Р data-win-Ьind="innerText:arti cl eText"></p> </div> </div> <div i d="fvArt i cl es • data-win-control ="WinJS.UI.F l ipView• data-win-opti ons ="{ i t emDataSource: MyData.l i stArt i cl es.dataSource, i t emTemplat e: sel ect ('#tmplArt i cl e') }"> </div> </body> </html > Элемент управления Fl ipView объявлен следующим образом: <div id="fvArt i cl es" data-win-control ="WinJS.UI.F l ipView• dat a-win-opti ons ="{ i t emDataSource: MyDat a.l i s tArt i c les.dataSource, i temTemplate: s el ect ('#tmplArt i c l e') } "> </div> Он привязан к списку статей с помощью свойства i temDa tasource. Элемент Fl ipView можно привязать к любому источнику данных, Глава 4. Элементы управления WinJS реализующему интерфейс I Lis tDataS ource. В библиотеке WinJS существует всего два таких объекта: WinJS. Binding. List и WinJS. UI. StorageDataSource. В приведенной выше разметке источником данных является спи­
сок статей типа WinJS. Binding. Li s t, а FlipView привязан к свойс­
тву dataSource объекта l i s tArticles, которое возвращает объект, реализующий интерфейс I Li s tDataSource. Свойство i temTemplate элемента Fl ipView указывает на элемент управления Teшplate с идентификатором tmplArticle, который встречался на странице раньше. Элемент Template содержит шаблон для форматирования статей, отображаемых элементом Fl ipView. Предупреждение. Объявляйте элемент Template раньше FlipView, иначе получите таинственную ошибку и потратите много часов на ее отладку. Предупреждение. Как обычно, не забывайте вызывать метод WinJS. UI. proces sAll ( ) , иначе элемент FlipView не появится. Отображение номеров страниц При перелистывании объектов в элементе FlipView бывает полезно знать, сколько всего есть объектов и сколько из них уже просмотрено. Другими словами, нам нужен номер страницы (рис. 4.23). Рис. 4.23. Отображение номера страницы в элементе Fl ipView Для получения номера текущей страницы (индекса текущего объекта) можно воспользоваться свойством currentPage элемента Элемент управления Fl i pView Fl ipView, а для получения общего количества страниц (числа объек­
тов в источнике данных для элемента) - методом count ( ). В листинге 4.1 6 приведена страница, содержащая FlipView и DIV Внутри элемента DIV показывается номер текущей страницы и об­
щее число страниц. Ли сти нг 4.1 6. Отображение номера текущей страницы в элементе Fl i pView (flipViewP ageNumber\flipViewPageNumber.html ) <div i d="tmplArt i c l e" dat a-win-control ="WinJS.Binding.Templ at e"> <div c l as s ="arti cl ei tem"> <h2 data-win-bind="innerText:t i t l e"></h2 > Author: <span data-win-bind="innerText:author"></span> <р data-win-bind="innerText:art i c l eText"></P> </div> </div> <div id="fvArt i cl es" data-win-control ="WinJS.UI.Fl ipView" data-win-opt i ons ="{ } "> </div> i temDataSource: MyDat a.l i s tArt i c les.dat aSource, i temTempl at e: select ('#tmplArt i cl e') <div id="divPageNumber"></div> Ли сти нг 4.1 7. Отображение номера текущей страницы в элементе Fl i pView (flipViewPageNumber\flipViewPageNumber.js) ( funct i on ( ) { "use stri ct"; func t i on ini t i al i z e ( ) WinJS.UI.proces sAl l ( ) .done ( funct ion ( ) { var fvArti cl es = document. getEl ementByid ( "fvArt i c l es") . '+winControl; var divPageNumber = document.getElementByid ("divPageNumber"); // Показать номер страницы и счетчик страниц funct ion updatePageNumber ( ) { var current Page = fvArt i cl es.current Page + 1; fvArti cl es. count ( ) . done ( funct i on ( count ) { divPageNumber.innerHTML = "Page " + current Page + н of и + count; } ) ; updatePageNumber ( ); //Обновить номер страницы при выборе новой ШJ••••• Глава 4. Элементы управления WinJS fvArticles.addEventListener ("pageselected", updatePageNumber); } ) ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Номер и счетчик страниц обновляются функцией updatePage­
Number ( ),код которой приведен в листинге 4.1 7. Эта функция изме­
няет содержимое элемента DIV с идентификатором divPageNumber. Отметим, что метод count ( ) элемента управления Fl ipView воз­
вращает не сам счетчик страниц, а обещание, при исполнении кото­
рого будет возвращен счетчик. Это несколько усложняет функцию updatePageNumber ( ). Создание нестандартных кнопок в элементе FlipView Иногда хочется создать необычные кнопки навигации в элементе FlipView. Стандартные кнопки ведут себя чересчур тонко: они не видны, пока пользователь не наведет на элемент управления курсор мыши. Возможно, вы предпочтете огорошить пользователя менее изысканным поведением. September v 21 22 23 24 25 l:t 26 27 29 30 2012 v Рис. 4.24. Нестандартные кнопки в элементе FlipView Для программного управления навигацией в элементе Fl ipView можно воспользоваться его методами previous ( ) и next ( ). На стра­
нице из листинга 4.1 8 имеются две кнопки с идентификаторами ЬtnPrevious и ЬtnNext. Элемент управления Fl i pView Ли ст инг 4. 1 8. Элемент управл ения Fli pView с нестандартными кнопками (flipViewB uttons\flipViewB uttons.html ) <di v i d="tmplArt i c l e" data-win-control ="WinJS.Binding.Templat e"> <div c l as s ="art i cl ei tem"> <h2 data-win-bind="innerText:t i t l e"></h2 > Author: <span data-win-bind="innerText:author"></span> <р data-win-Ьind="innerText:art i cl eText"></p> </div> </div> <div id="fvArt i c l es" data-win-control ="WinJS.UI.Fl ipView" data-win-opt i ons="{ i temDataSource: MyDat a.l i stArt i c l es.dataSource, i t emTemplate: select ('#tmplArt i cl e') } "> </div> <div id="divNavigat i on"> <butt on id="btnPrevious">Previous</button> <butt on id="btnNext">Next</butt on> </div> JavaScript-кoд в листинг 4.1 9 присоединяет обработчики со­
бытий к кнопкам ЬtnPrevious и ЬtnNext. При щелчке по кнопке ЬtnPrevious вызывается метод previous ( ), а при щелчке по кнопке ЬtnNext - метод next ( ). Лист инг 4.1 9. Эл емен т управ ления FlipView с не стандартн ыми кнопками (flipViewButtoпs\fl i pViewButtons.j s) ( funct i on ( ) { "use stri ct"; funct i on ini t ial i z e ( ) WinJS.UI.processAl l ( ) .done ( funct i on ( ) var fvArt i cl es = document.getEl ementByid ("fvArt i cl es"). <+winControl; var btnPrevious = document.getElementByid ("btnPrevious"); var btnNext = document.getElementByid ("btnNext"); 1 1 Настроить кнопки btnPrevious.addEventLi s t ener ("c l ick", funct i on ( ) { fvArt i cl es.previous ( ); } ) ; btnNext.addEventLi s t ener ("c l ick", funct ion ( ) { fvArt i cl es.next ( ); } ) ; Глава 4. Элементы управления WinJS } ) ; document.addEventLi s t ener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Р ез юме В этой главе мы занимались простыми элементами управления из библиотеки WinJS. Я рассказал о возможностях элементов Tool tip, ToggleSwitch, Rating, DatePi cker, TimePi cker и Fl ipView. Вы научились создавать элементы управления декларативно и императивно. Кроме того, на примерах было продемонстрировано, как эти элементы используются в различных ситуациях. Но это далеко не все элементы управления! У нас еще есть, с чем поиграть! В следующих главах я расскажу о других элементах WinJS - меню и списке Lis tView. Но сначала поговорим о формах. - -
rЯАВА 5. Со эд ание фо рм В этой �лаве: • Средства контроля данных в формах HTMLS.
• Элементы ввода данных в HTMLS.
• Создание редактора обогащенного текста.
• Показ хода выполнения.
Я полагаю, что читатель этой книги уже знаком с основами HTML, но знакомство с последними новшествами, появившимися в HTMLS, не предполагается. В частности, я не предполагаю, что вы знаете о том, какие изменения в НТМL-формах включены в стандарт HTMLS. В первом разделе этой главы я расскажу о том, как воспользовать­
ся появившимися в HTMLS средствами контроля данных формы в приложениях Магазина Windows. Вы узнаете об атрибутах контро­
ля - в частности, requi red и pattern, - определяющих ограничения на дан ны е формы. Затем мы обсудим новые возможности элементов ввода данных, включенные в HTMLS. Вы узнаете, как с их помощью контролиро­
вать тип вводимых данных. Наконец, я расскажу о двух важных нововведениях в HTMLS. Я объясню, как с помощью атрибута contenteditaЫe создавать элементы ввода, принимающие текст с дополнительной разметкой, например полужирный или курсивный. Кроме того, мы рассмотрим элемент progre s s. Сре дст ва контроля данных в формах HTML5 В приложении Магазина Windows для контроля данных формы про­
ще всего воспользоваться атрибутами контроля, включенными в Глава 5. Создание форм HTMLS. Пусть, например, требуется создать НТМL-форму, в кото­
рой есть поля для ввода страхового номера. В этом случае можно ука­
зать атрибут required, означающий, что поле обязательно, и атрибут pattern, задающий образец страхового номера. Атрибут required В следующем фрагменте демонстрируется использование атрибута requi red: < form> <div> <l abel > Soc ial Security Number: < input i d="s sn" required /> </l abel > </div> <div> <input type="submit" /> </div> </f orm> Если попытаться отправить эту форму, не заполнив поле ssn (страховой номер), то будет выдано сообщение об ошибке, показанное на рис. 5.1. Поле ввода окружено красной рамкой, а сообщение отображается в выноске. Рис. 5.1. Использование атрибута контроля req ui r ed Атрибут pattern Атрибут pattern позволяет сопоставить поле ввода с образцом - ре­
гулярным выражением. Так, в следующем фрагменте проверяется, что страховой номер соответствует регулярному выражению: < form> <div> <l abel > Social Secur i ty Number: <input i d="s sn" Средства контроля данных в формах HTML5 required pattern= • л\d { 3 } -\d { 2 } -\d { 4 } $" t i t l e="###-##-####" /> <!l abel > </div> <div> < input type= • submi t • /> </div> </f orm> 111••Ш1 Отметим, что для поля ssn заданы оба атрибута: requi red и pattern. Сопоставление с образцом pattern не производится, если в поле не введено значение. Кроме того, у поля ssn имеется атрибут ti tle, который содержит ожидаемый формат, отображаемый в сообщении об ошибке сопостав­
ления с образцом. При вводе неправильного страхового номера будет выдано сооб­
щение об ошибке, показанное на рис. 5.2. Social Security Numbe �нl!'ii!!!J�н� appl� Х You nшst use th1s forniat. ###-##-� Рис. 5.2. Использование атрибута контроля pattern Примечание. Я nредnочитаю искать нужные мне регулярные выражения на сайте http://regexl i b.com. Нестандартный контроль данных Чтобы связать с элементом формы нестандартные правила контроля, можно воспользоваться JаvаSсгiрt-методом s etCus tomValidi ty ( ). Он позволяет ассоциировать сообщение об ошибке контроля данных с полем формы. Пусть, например, имеется сложный набор правил для проверки имени пользователя в форме регистрации. Например, требуется, что­
бы имя имело определенную длину, было уникально в базе данных и не содержало специальных символов. Прекрасная возможность для применения нестандартного контроля. В следующей НТМL-форме имеется поле useгName: m1•••11 < form> <div> <l abel > User Name: <input i d="us erName" required /> <!label > </div> <div> <input type="submi t" /> </div> </form> Глава 5. Создание форм На примере следующего JavaSciipt-кoдa демонстрируется, как вывести сообщение об ошибке, если имя пользователя слишком ко­
роткое: var userName = document.getElementByi d ("userName"); userName.addEventLi s t ener ("input", funct i on ( evt ) { 1 1 Имя пользователя должно содержать больше трех символов if ( userName.value.l ength < 4 ) { } } ) ; userName. setCustomVal idity ( "User name too short ! "); el s e { userName.s etCust omval i di ty (""); Здесь создается прослушиватель события input. Если значение элемента input изменилось, то проверяется длина значения. Если оказалось, что имя пользователя короче 4 символов, то вызывается метод setCustomVal idi ty ( ),который выдает сообщение об ошибке. В противном случае вызывается тот же метод, но с пустой строкой в качестве аргумента, - тогда все ошибки, ранее ассоциированные с этим полем, стираются. Примечание. Событие input генерируется при каждом изменении значе­
ния поля ввода. Событие input отличается от события change, которое не генерируется, пока элемент не потеряет фокус. Сообщение об ошибке контроля появляется только при попытке отправить форму. В этот момент вы увидите сообщение, показанное на рис. 5.3. Рис. 5.3. Нестандартный контроль данных Средства контроля данных в формах HTML5 lll••ВD Настройка стиля сообщения об ошибке По умолчанию поля НТМL-формы, содержащие ошибки, окаймля­
ются красной рамкой. Так будет, к примеру, когда вы пытаетесь отпра­
вить форму, не заполнив обязательное поле. Но внешний вид полей формы в различных состояниях, относя­
щихся к корректности данных, можно настроить с помощью следую­
щих псевдоклассов CSS: : valid - применяется к правильно заполненным элементам input; : invalid - применяется к неправильно заполненным элемен­
там input; : required - применяется к обязательным элементам input (имеющим атрибут requi red); : optional - применяется к необязательным элементам input (не имеющим атрибута requi red). Рассмотрим следующую форму регистрации пользователя: < form> <div> <l abel > Fi rst Name: <input i d="f irstName" required /> </label > </div> <div> <l abel > Las t Name: <input id="lastName" required /> <!l abel > </div> <div> <l abel > Company: <input id="company" /> <!l abel> </div> <div> <input type="submi t" /> </div> </form> В этой форме поля имени и фамилии обязательны. Кроме того, она содержит необязательное поле для ввода компании, в которой ра­
ботает пользователь. Для стилизации элементов input можно задать следующие прави-
ла: 1Ш• • 1 11 Глава 5. Создание форм :val i d { background-col or: green; :invali d background-color: yel l ow; :opti onal border: 4рх s ol i d green; :required border: 4рх s ol i d red; При таких правилах правильно заполненные поля будут иметь зе­
леный фон, а неправильно - желтый. Необязательные поля окаймля­
ются зеленой рамкой, а обязательные - красной. Сброс формы После успешной отправки формы часто имеет смысл сбросить ее в исходное состояние, чтобы можно было заполнить и отправить ее заново. Например, если имеется приложение, в котором форма ис­
пользуется для ввода сведений о кинофильмах, то после добавления одного фильма форму следует подготовить для ввода следующего. Но если применяются атрибуты контроля, то сбросить форму, просто записав в каждое поле пустые строки, не получится. Если за­
писать в обязательное поле пустую строку, то оно окажется в некор­
ректном состоянии. Вместо этого следует вызвать JavaScгipt-мeтoд reset ( ),который переводит форму в исходное состояние. Вот, например, простая форма для ввода названия фильма: < form i d="frmAdd"> <div> <l abel> Tit l e: < input id="inpTi t l e" required /> </l abel > </div> <div> < input type="submi t" /> </div> </f orm> А вот JavaSc1·ipt-кoд для обработки события отправки: Элементы ввода данных в HTML5 ( funct i on ( 1 { "use stri ct"; funct i on ini t i al i z e ( ) var frmAdd = document.getElementByid ("frmAdd"); var inpTi t l e = document.getEl ementByid ("inpTi t l e"); frmAdd.addEventLi s tener ("submit", funct i on ( evt l { evt.preventDefaul t ( ); var newMovie = { t i t l e: document.getEl ementByid ("inpTi t l e") .value } ; addMovi eToDb ( newMovi e ) .done ( func t i on ( ) { frmAdd.reset ( ); } ) ; } ) ; func t i on addMovi eToDb ( newMovie ) { return new WinJS.Promi s e ( func t i on ( compl et e ) { 1 1 Добавить в базу данных complete ( ); } ) ; document.addEventLi s t ener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Здесь мы добавляем название фильма в базу данных IndexedDB, а затем сбрасываем форму в исходное состояние методом reset ( ). Предупреждение. Если не вызвать в обработчике метод evt. preven t­
Def aul t ( ), то форма будет отправлена, а вся страница перезагружена. В приложениях Магазина Wi ndows мы обычно вообще избегаем отправлять форму методом submi t ( ) . Эле менты вв ода данных в HTM L5 Любой, кто работал с HTML, знает о стандартных элементах ввода, на­
пример <input type="text" /> или <input type="checkbox" />. У элементов разных типов различается внешний вид, и данные они также принимают разные. В рекомендации по HTMLS добавлено несколько новых типов элементов ввода: searcl1 tel шl email • datetime date moвth week time datetime- local вumЬег гап gе соlог Глава 5. Создание форм Этими новыми типами можно воспользоваться для контроля данных. Например, в поле <input type="number" /> можно ввести только цифры, а в поле <input type="url" /> - только правильный абсолютный Ul�L-aдpec. Новые типы позволяют также управлять пользовательским интерфейсом ввода данных. Например, если используется сенсорная клавиатура, то при входе в поле <input type="emai l" /> на экране появляется специальная клавиатура для ввода адресов электронной почты, на которой есть клавиши @ и .com (рис. 5.4). q w е r t у u i о р <Е1 а s d f 9 h j k J • [11ter 1' z х с v Ь n m , . ? 1' &123 Ct1I @} @ .cum < > ffiDJ Рис. 5.4. Специальная сенсорная клавиатура для ввода адресов электронной почты В этом разделе вы узнаете, как пользоваться новыми возможнос­
тями элементов ввода в HTML5. Примечание. На момент написания этой книги браузер I E1 О поддерживал еще не все средства НТМL5-форм. В частности, не поддерживаются типы для ввода даты, времени и цвета. Метки полей формы Прежде всего, вы должны знать, как правильно помечать поля НТМL­
формы. Это важно для того, чтобы сделать ваше нриложение доступ­
ным людям с ограниченными возможностями, - так спо не пренебре­
гайте метками. Элементы ввода данных в HTML5 111••11Ш Пометить элемент формы с помощью элемента label можно дву­
мя способами. Если вы хотите, чтобы метка находилась радом с эле­
ментом, то можете поместить сам элемент между открывающим и за­
крывающим тегом label: <l abel> Ti tl e: <i nput i d="inpTit l e" required /> </l abel> Если метка отделена от помечаемого элемента, то можно связать их явно, добавив в тег label атрибут for: <l abel for="inpTi t le"> Tit l e: </l abel > ... Прочее содержимое ... < input id="inpTi t l e" required /> Помечать все элементы формы нужно в любом случае, чтобы с приложением могли работать пользователи с ограниченными воз­
можностями. Но если требуется снабдить какой-то элемент ввода дополнительной информацией, то можно воспользоваться новым ат­
рибутом placeholder, появившимся в HTMLS. В приложении Мага­
зина Windows этот атрибут создает водяной знак. Например, в следующей форме имеется поля для ввода кода акти­
вации продукта: <l abel> Act ivat i on Code: < input id="act ivat i onCode" s i z e="l 0" placeholder="##-####-##" /> </label > Атрибут placeholder в этом поле содержит строку «##-####­
##1> (рис. 5.5). Как только вы начнете вводить в поле данные, эта строка исчезнет. Act1vation Code -
Рис. 5.5. Использование атрибута placeholder Примечание. Для стилизации текста, отображаемого атрибутом place­
holder, можно использовать nсевдокласс CSS : -ms -input-placeholder. ШJ•• • •� Глава 5. Создание форм Ввод чисел Если вы хотите, чтобы в поле можно было ввести только число, то воспользуйтесь атрибутом type="number": <l abel> Favori te Number: <input i d="inpFavNumber" type="number" placeholder="###" /> <!l abel> Если в поле с типом number ввести что-то, отличающееся от числа, то введенное значение исчезнет, как только поле потеряет фокус. Такое поведение может оказаться неожиданным для пользователя. Поэтому имеет смысл включать также атрибут placeholder или пояснительный текст, говорящий о том, что в поле можно вводить только числа (без знака доллара - только само число). Атрибуты min и max позволяют задать минимальное и максималь­
ное значение поля ввода: <l abel > Quant i ty: <input id="inpQuant i ty" type="number" placehol der="###" min="l" max="1 0" /> </l abel > При попытке ввести число, выходящее з а пределы указанного диапазона, выводится сообщение об ошибке (рис. 5.6). х You must enter а value bet\veen 1 and 10 Рис. 5.6. Число вне диапазона По умолчанию в поле типа <input type="number" /> можно вво­
дить только целые числа. Чтобы ввести нецелое число, например 1.5, нужно добавить атрибут s tep (шаг): <input id="inpFavNumber" type="number" step="0.5" placeholder ="###" /> Элементы ввода данных в HTML5 111••1Ш1 Атрибут s tep задает допустимое приращение. Если числовое поле встречается в приложении Магазина Windows и это приложение за­
пущено на устройстве с сенсорной клавиатурой, то при входе в поле появляется специальная клавиатура для ввода чисел (рис. 5.7). r.tl> @ # $ % & 2 з GJ + 4 5 б .... е \ 1 7 8 9 111 @/ < > Spacc о -
Рис. 5. 7. Ввод числа на сенсорной клавиатуре Ввод числа из заданного диапазона Чтобы показать ползунок, нужно поместить на страницу элемент <input type="range" />.Например,вследующейформеотображается ползунок, позволяющий выбрать количество покупаемых конфет: <l abel> Quant i ty o f Candy: <input id="quant ity" type="range • min="1 0" max="l 0 0" s tep="5" value="3 0"/> <!l abel > Ползунок показывает диапазон от 1 0 до 1 00 с шагом 5. По умолча­
нию установлено значение 30 (рис. 5.8). QuantJty of Candyc -. � Рис. 5.8. Отображение ползунка Ввод адресов электронной почты, URL, телефонов и поисковых запросов Типы полей ввода email, url, tel и search позволяют вводить соот­
ветственно адреса электронной почты, URL-aдpeca, номера телефо­
нов и поисковые запросы. Глава 5. Создание форм Если используется сенсорная клавиатура, то при входе в поле типа <input type="emai l" /> появляется специальная клавиатура с кла­
вишами © и .com (рис. 5.9). Рис. 5.9. Сенсорная клавиатура для ввода адресов электронной почты Тип <input type="emai l" /> обеспечивает также автоматичес­
кий контроль. При попытке ввести недопустимый адрес электронной почты появляется сообщение об ошибке (рис. 5.1 0). Рис. 5.1 О. Контроль ввода адреса электронной почты Тип <input type="url" /> предназначен для ввода URL-aдpecoв. На экране отображается сенсорная клавиатура с клавишами / и .com (рис. 5.1 1 ). Рис. 5. 1 1. Сенсорная клавиатура для ввода URL-aдpecoв Примечание. Сенсорная клавиатура появляется, только если к устройству не подключена обычная клавиатура или если пользователь явно запросит ее. Тип <input type="url" /> позволяет вводить только абсо­
лютные URL-aдpeca. К примеру, адреса !1ttp:j /Sнpe1·expeгt.com и ftp://Sнpeгexpeгt.com допустимы, а адреса SLtpeгexpeгt.coш и wwv v. Элементы ввода данных в HTMl5 supeгexpeгt.com - нет, потому что они не являются абсолютными (рис. 5.1 2). Рис. 5.1 2. Контроль ввода URL-aдpeca Тип <input type="tel" /> применяется для ввода телефонных номеров. Поскольку существует много разных форматов телефонных номеров, то никакого контроля не производится. Для поля такого типа просто выводится специальная сенсорная клавиатура, показан­
ная на рис. 5.1 3. Tat_) @ # $ % & 2 3 <Е1 + 4 5 б +" е \ / 7 8 9 • @) < > Spac:::e о -
Рис. 5.1 З. Сенсорная клавиатура для ввода телефонных номеров И наконец, тип <input type="s earch" />.О нем сказать нечего, потомучто ведет онсебяточнотакже,кактип <inрut type="text" />. Для нег о н ет ни специальной клавиатуры, ни какого-либо контроля. Ввод значения из списка Появившийся в HTML5 атрибут l i s t позволяет выполнять автома­
тическое завершение ввода значения. В примере ниже на выбор пред­
лагается три марки автомобилей: < l abel > Car Make: <input id="inpCarMake" l i s t ="dlCarMakes" /> <datal i s t id="dlCarMakes"> <option>BMW</opt i on> <opt i on>Ford</opti on> <option>Tes l a</opt i on> </datal i s t > </l abel> Ш1••••1 Глава 5. Создание форм Атрибут l i s t указывает на элемент HTML5 datalis t, который содержит список предлагаемых вариантов. Когда пользователь начи­
нает вводить текст в такой элемент, он получает список вариантов, как показано на рис. 5.1 4. Рис. 5.1 4. Получение списка предлагаемых вариантов Можно вводить и значения, отсутствующие в списке. Наличие атрибута l i s t превращает элемент скорее в комбинированный, чем в фиксированный список. Выбор файла Элемент типа <input type="file" /> применяется для открытия окна выбора файла. Например, таким образом пользователю можно предоставить возможность выбрать графический файл с жесткого диска. Ниже показан фрагмент НТМL-страницы, в котором есть эле­
мент <input type="file" /> и IMG с идентификатором imgPi cture. Картинка, выбранная на жестком диске, появляется в элементе IMG (рис. 5.1 5). < form id="frmAdd"> <div> <l abel > Picture: <input id="inpFi l e" type="f i l e" accept ="image/*" /> </l abel > <input type="submi t" /> </div> </form> <img id="imgPicture" /> Вэлемент <inрut type="file" /> можно включить атрибут ассерt, налагающий ограничения на то, какие файлы разрешается выбирать. Например, если присвоить этому атрибуту значение "image/*", то нельзя будет выбрать никакие файлы, I<роме графических. Элементы ввода данных в HTML5 111 ••Ш11 Рис. 5. 1 5. Выбор графического файла Показанный ниже JаvаSсгiрt-код обрабатывает событие отправки формы. Он получает выбранную картинку из коллекции files эле­
мента выбора файла и отображает ее в элементе I MG: ( funct i on ( ) "use stri ct"; funct i on ini t i al i z e ( ) var frmAdd = document.getElementByid ("frmAdd"); frmAdd.addEventLi stener ("submi t", funct i on ( evt ) evt.preventDe f aul t ( ); var imgPicture = document.getElementByid ("imgPicture"); var inpFi l e = document.getEl ementByi d ("inpFi l e"); i f ( inpFi l e.f i l es.l ength > 0 ) { } } ) ; 1 1 Использовать HTMLS F i l e API для создания URL объекта, 1 1 ссылающегося на файл картинки var pi ctureUrl = URL.createObj ectURL ( inpFi l e.f i l es [ O ] ); 1 1 Показать картинку в элементе IMG imgPi cture.src = pictureUrl; document.addEventLi s tener ("DOMCont entLoaded", ini t i al i z e ); } ) ( ); Соз дание редактора обогащенного те кс та Глава 5. Создание форм Для создания в форме поля, способного принимать обогащенный текст - например, выделенный полужирным шрифтом, курсивом или подчеркиванием - предназначен атрибут contentedi tаЫе. Пусть требуется создать страницу, на которой пользователь сможет ввести текст сочинения, необходимого для приема в колледж. Для этого по­
дойдет такая НТМL-форма: < form> <l abel for="inpEs s ay"> Col lege Admi s s ions Es s ay: <!l abel > <div id="inpEs s ay" contentedi taЫe ="true" cl as s ="win-textarea richt ext"></div> <br /> <input type="submi t" /> </form> Здесь к элементу DIV с идентификатором inpEs say применен атрибут contentedi tаЫе, который делает DIV редактируемым (рис. 5.1 6). College AdmTssюns Essay: Dearest MI T, л Please consider me for adшissions ta your un.i,·ersity. I ha\"e а 4.5 GPA and I � like math.I 1 v Рис. 5.1 6. Создание редактора обогащенного текста Чтобы применить к тексту выделение полужирным шрифтом, нажмите Ctгl+b, для выделения курсивом - Ctгl+i, а для подчерки­
вания - Ctгl+u. Отметим, что к элементу DIV с атрибутом contenteditaЫe применено два СSS-класса: win- textarea и richtext. Класс win­
textarea включен в состав библиотеки WiпJS и применяет I< редак­
тору стандартные стили приложения Магазина Wiпdows. Показ хода выполнения 111 ••1Ш1 Класс richtext определен следующим образом: .ri cht ext { width: З О Орх; height: 1 0 0рх; whi t e- space: pre-wrap; В рекомендации по HTMLS говорится, что для элемента DIV с атрибутом contenteditaЫe всегда следует задавать стиль white­
space: pre-wrap, а кто я такой, чтобы усомниться в мудрости авторов рекомендации? Вот я и добавил whi te- space: pre-wrap, как велено. Показ хода выполнения В рекомендации по HTMLS описан новый элемент progres s для отображения индикатора хода выполнения. Есть два основных типа таких индикаторов: детерминированный и недетерминированный. Недетерминированный индикатор применяется, когда нужно по­
казать, что программа занята, но сколько времени займет выполнение задачи, неизвестно. Такой индикатор объявляется следующим обра­
зом: <progress i d="progres s l"></progres s > В приложениях Магазина Windows индикатор хода выполнения по умолчанию отображается в виде перемещающихся по горизонтали точек (рис 5.1 7). Рис. 5. 1 7. Недетерминированный индикатор хода выполнения Для отображения детерминированного индикатора нужно задать атрибуты max и value. Ниже приведен пример индикатора, изменя­
ющегося от 1 до 20: <progress id="progres s l" max= 11 2 0 11 value="l"> c/progres s > Глава 5. Создание форм А вот JavaScгipt-кoд, который обновляет ход выполнения один раз в секунду: ( funct ion ( ) { "use stri ct"; funct i on ini t i al i z e ( ) var progres s l = document.getElementByid ("progres s l"); var ivl Progres s = window.set int erval ( updateProgres s, 1 0 0 0 ); funct i on updateProgres s ( ) { progre s s l.value = + +progre s s l.value; i f ( progre s s l.value === 2 0 ) { window.c l earint erval ( ivl Progres s ); document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); При таком объявлении индикатора рисуется традиционная по­
лоска (рис. 5.1 8). Рис.5.1 8.Детерминированный индикатор хода выполнения Существует несколько стилей, применимых к индикатору хода выполнения в приложениях Магазина Windows. Для недетермини­
рованного индикатора можно задать класс win-ring, который отоб­
ражает точки, движущиеся по окружности, а н е по горизонтальной прямой (рис. 5.1 9). cprogress id="progres s 2" c l as s ="win-ring"> </progres s > • Рис. 5. 1 9. Точки, движущиеся по окружности Для управления размером недетерминированного индикатора предназначены классы win-medium и win- large. Ниже приведен пример трех индикаторов хода выполнения с возрастающими разме­
рами: Резюме <progres s id="progres sRingl" cl as s ="win-ringн > </progres s > <br /><br /> <progress i d="progres sRing2" c l as s ="win-ring win-medium"> </progres s > < br />< br /> <progress id="progres sRingЗ • cl as s ="win-ring win- l arge"> </progres s > Р ез юме Эта глава была посвящена созданию НТМL5-форм в приложениях Магазина Windows. В первом разделе я рассказал о новых средствах контроля правильности данных, которые появились в HTMLS. Вы научились применять атрибуты requi red и ра t tern для выполнения простых проверок и метод setCustomVal idi ty ( ) - когда требуется нечто более сложное. Затем я описал достоинства новых типов элементов ввода в HTML5. Вы узнали, как определять поля для ввода адресов элект­
ронной почты, URL-aдpecoв, телефонных номеров и поисковых за­
просов. Я также показал, как выбирать файлы с жесткого диска с по­
мощью элемента <input type="file" />. Наконец я продемонстрировал создание редактора обогащенного текста с помощью атрибута contentedi tаЫе и индикатора хода вы­
полнения - с помощью нового элемента progre s s. rЯАВА 6. М ен ю и вс nnы ва ющ ие
эn е м ен ты В этой главе: • Элемент управления Flyout.
• Элемент управления Menu.
• Элемент управления АррВаг.
• Задание настроек приложения.
• Отображение диалоговых окон Windows.
В этой главе мы будем заниматься всплывающими элементами, меню, панелями инструментов, настройками и диалоговыми окнами. Вы уз­
наете, как пре длагать пользователям выбор и узнавать об их решении. Я начну с использования элементов управления Flyout и Menu, которые позволяют показать набор вариантов прямо в теле страницы. Вы узнаете, как отображать в элементе Menu кнопки, переключатели, всплывающие элементы и разделители. Затем я перейду к панели приложения. В приложениях Магазина Windows это стандартное место для размещения команд приложения. Вы научитесь создавать глобальные команды и команды выбора. Мы также рассмотрим вопрос о задании настроек приложения и, в частности, о том, как воспользоваться элементом управления Set t i ngs Fl y out, чтобы расширить набор стандартных настроек, отображаемых на чудо-кнопке Settings. Наконец, я покажу, как создавать модальные диалоговые окна с помощью класса Mes s ageDialog. Вы научитесь создавать диалоги с предупреждениями и диалоги Да/Нет. Элемент управления Fl yout 11• • •1&1 Эле мент управл ения Flyout Элемент управления Flyout используется для отображения всплы­
вающего окна на странице. Это окно пропадает при щелчке мышью в любой точке вне него (или после нажатия клавиши Esc). Элемент Flyout можно применять для вывода информационных сообщений и предупреждений или для ввода данных. Примечание. Элемент управления Flyout подцерживает так называемое авт оматическое исч езновение. При щелчке вне области элемента он авто­
матически пропадает с экрана. Чаще всего всплывающие элементы применяются для вывода пре­
дупреждений. Например, если в вашем приложении имеется кнопка «Безвозвратно удалить все данные:.>, то, наверное, имеет смысл пре­
дупредить пользователя во всплывающем окне, перед тем как оста­
вить его без данных. Еще одно типичное применение всплывающих элементов - отоб­
ражение форм поверх основного текста страницы. Например, в лис­
тинге 6.1 показан элемент Flyout, который позволяет выбрать шрифт (рис. 6.1 ). Рис. 6. 1. Отображение элемента Flyout Листинг 6. 1. Отображение элемента Flyout (flyout\flyout.html ) <!- - Элемент управления Flyout - - > <div i d="f lyТypefac e" data-win-control ="WinJS.UI.Flyout"> <l abel>Select Тypeface:</label > <s el ec t id="s el ectТype f ac e"> <opt i on>Arial</opti on> <opt i on>Impact</opt i on> <opt i on>Comic Sans MS</opti on> </sel ect> </div> <!- - Кнопка, открывающая элемент F lyout - - > ШJ•• 1 1;1 Глава 6. Меню и всплывающие элементы <button id="btnТypef ace">Select Typeface</button> <!- - Стилизуемый текст - - > <Р i d="pText"> Lorem ipsum dolor s i t amet, cons ect etuer adipi s c ing el i t e Maecenas port t i t or congue mas s a. Fusc e posuere, magna sed pulvinar ultricies, purus lectus malesuada l ibero, sit amet commodo magna eros qui s urna. </р> На странице из листинга 6.1 имеется кнопка, которая открывает всплывающий элемент. В нем показан список шрифтов. После выбо­
ра шрифта текст-«рыба:.> перерисовывается новым шрифтом. Приведенный в листинге 6.2 код демонстрирует настройку кноп­
ки для открытия всплывающего элемента. Обработчик щелчка по кнопке вызывает метод show ( ) элемента F l y out, который показывает элемент на экране. Методу s h ow ( ) следует передать элемент-якорь, который опре­
деляет, в каком месте страницы должен появиться элемент Fl yout. В листинге 6.2 элемент Flyout привязан к кнопке. Лис т инг 6.2. Отображ ение эл емента Flyout (flyaut\flyout.js) ( funct i on ( ) { "use stri ct"; funct i on initial i z e ( ) WinJS.UI.processAll ( ).done ( funct ion ( ) { var btnTypef ace document.getElementByid ("btnTypef ac e"); var f lyTypeface = document. getEl ementByid ( "f lyTypefac e") . '-+wincontrol; var selectТypeface = document.getElementByid ("selectТypeface"); var pText = document.getElementEyid ("pText"); //Присоединение обработчика для показа Flyout btnТypeface. addEventLi s t ener ( "c l ick", funct i on ( ) { f lyТypeface.show ( btnТype face ); } ) ; //Присоединение обработчика для выбора шрифта sel ectType face. addEventLi s t ener ( "change", func t i on ( ) { pText.s tyle.f ontFami ly = selectТype face.value; } ) ; } ) ; document. addEventLi s t ener ( "DOMContentLoaded", ini t i al i z e ) ; } ) ( ); Элемент управления Menu •••••1&1 Предупреждение. Не забудьте вызвать метод WinJS. ur. proces sAl l ( ), иначе элемент Flyout никогда не всплывет. Эле мент управл ения Menu Элемент управления WinJS Menu является производным от Flyout, поэтому у них много общего. Как и Flyout, элемент мenu появляется в виде всплывающего окна, которое автоматически исчезает, если щелкнуть мышью вне него. Однако в отличие от Flyout, элемент Menu предназначен специ­
ально для вывода меню команд. Поэтому в него можно помещать только команды меню, никакие другие элементы управления или НТМL-элементы не допускаются. Поддерживаются следующие типы команд меню: • кнопка - щелчок по такому пункту меню приводит к выполне­
нию некоторого действия; переключатель - состояние пункта меню меняется на противо­
положное; всплывающий элемент - щелчок по такому пункту меню при­
водит к появлению всплывающего элемента; разделитель - разделяет группы команд . В листинге 6.3 показан элемент управления Menu, содержащий при­
меры команд различных типов. Листинг 6.3. Отображение меню ( meпu\meпu.html ) <!- - Щелчок по этой кнопке выводит элемент Menu - - > <button id="btnEdi t">Edi t </but ton> <!- - Элемент управления Menu - - > <div id="menuEdi t" dat a-win-control="WinJS.UI.Menu"> <button data-win-control ="WinJS.UI.MenuCommand" data-win-opt ions="{ i d: 'menuCommandDelete', l abel: 'Delete' , type: 'button' }"></button> <hr data-win-contro l ="WinJS.UI.MenuCommand" dat a-win-opti ons ="{ type: 's eparator' }" /> <butt on Глава 6. Меню и всплывающие элементы c1ata-win-control ="WinJS.UI.MenuComrnand" dat a-win-opti ons ="{ i d: 'rnenuComrnandBold' , label: 'Bold' , type: 't oggle' }"></butt on> <button dat a-win-control ="WinJS.UI.MenuComrnand" data-win-opt i ons="{ i d:'rnenuComrnandi t al ic', label: 'I tal i c', type: 'toggle' }"></but t on> <button dat a-win-control ="WinJS.UI.MenuComrnand" data-win-opti ons ="{ id: 'rnenuComrnandТypef ac e', label: 'Typeface' , type: 'f lyout', f lyout: select ('#f lyТype f ac e') }"></butt on> </div> <!- - Элемент управления Flyout - - > <div id="f lyТypefac e" data-win- control ="WinJS.UI.Flyout"> <l abel >Select Тypeface:</l abel > <s el ec t id="s el ectТypef ac e"> <opt i on>Arial</opti on> <opt i on>Irnpact </opti on> <opti on>Cornic Sans MS</opti on> </select> </div> <!- - Стилизуемый текст - - > < Р id="pText"> Lorern ipsurn dolor s i t arnet, consectetuer adipi s c ing el i t e Maecenas portt itor congue rnas sa. Fus ce posuere, rnagna s ed pulvinar ultrici es, purus lectus rnal esuada l ibero, s i t arnet c omrnodo rnagna eros qui s urna. </р> На странице из листинга 6.3 имеется текст-«рыба». Чтобы изме­
нить его внешний вид, используется меню (рис. 6.2). Меню содержит пять команд (экземпляров класса WinJS. ur .MenuComrnand): кнопку, разделитель, два переключателя и всплы­
вающий элемент. Кнопка удаляет весь текст. К этой команде присоединен обработ­
чик события, 110ю1занный в листинге 6.4. Элемент управления Menu Рис. 6.2. Использование элемента Menu Команда «разделитель» не делает ничего, только выводит гори­
зонтальную черту между группами команд. Переключатели меняют начертание шрифта: обычное-полужир­
ное и обычное-курсив. Обработчики этих команд приведены в лис­
тинге 6.4. Наконец, команда «всплывающий элемент» показывает на экране элемент управления Flyout. Этот элемент включен в НТМL-стра­
ницу из листинга 6.3 и имеет идентификатор flyTypeface. Элемент Flyout позволяет выбрать шрифт текста (рис. 6.3). Когда появляется элемент Flyout, меню исчезает. Ли сти нг 6.4. Отображ ение меню (menu\menu.js) ( funct i on ( 1 { "use s tri ct"; funct i on initial i z e ( ) WinJS. UI. processAll ( ) . done ( funct ion ( ) { var btnEdi t = document.getElementByid ("btnEdit"); var menuEdit = document.getElementByi d ("menuEdit"). '+winControl; var selectТypeface = document.getElementByid ("selectТypeface"); var pText = document.getElementByid ("pText"); 1 1 Нажатие кнопки Edit выводит элемент Menu btnEdi t.addEventLi s t ener ("c l i ck" , funct i on ( ) { menuEdi t.show ( btnEdit ); } ) ; 1 1 Присоединяем обработчики команд document.getElementByid ("menuCommandDel ete") .addEventLi s t ener ("cl i ck" , func t i on ( evt ) pText.innerHTML = "[ delet ed]" ; } 1; document. getEl ementByi d ( "menuCommandBold") Глава 6. Меню и всплывающие элементы .addEventLi s t ener ("c l i ck" , func t i on ( evt ) var toggleState = document.getElementByid '-+-( "menuCommandBold") . winControl.selected; i f ( t oggleStat e ) { pText.s tyl e.fontWeight el s e { } } ) ; pText.s tyl e.fontWei ght 'bol d' 'norrnal' document.getElementByid ("menuCommandi tal i c") .addEventLi s tener ("c l i ck" , func t i on ( evt ) var toggleState = document.getElementByid '-+-( • menuCommandi t al i c"). winControl.selected; i f ( t oggleStat e ) { pText.s tyl e.f ontStyle el s e { } } ) ; pText.s tyl e.f ontStyl e 'i tal i c' 'normal' selectTypeface.addEventLi s t ener ("change •, funct ion ( ) pText.s tyle.f ontFami ly = selectTypeface.value; } ) ; } ) ; document.addEventLi stener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Edit odiplscing Select Typeface: Рис. 6.3. Использование элемента Menu для отображе ния всплывающего элемента Эле мент у правления Ap pBar В приложениях Магазина Wi11dows есть стандартное место для раз­
мещения команд: панель приложения. Обысн-ю она находится в ниж-
Элемент управления AppBar 1лl • ••m1 ней части области, занятой приложением. Однако можно показать две панели - сверху и снизу. Панель приложения становится видна, только если провести пальцем снизу вверх или сверху вниз или щел­
кнуть правой кнопкой мыши. На рис. 6.4 показаны две панели приложения в версии Inteшet Ех­
рlогег для Windows 8. На нижней панели находится адресная строка, а не верхней - миниатюры активных вкладок. Если щелкнуть мышью в любом месте страницы, то обе панели исчезнут и приложение снова будет занимать весь экран. Примечание. Поскольку верхняя панель обычно используется для перехода между страницами приложения, ее часто называют панелью навигации. �t>"'°<><'-"Hol к,.,. .... ,...,1м� " " ' . •t 'J ,,.., ) • '5"1 :. " ") l' ' -. • > -
, ....................................................... . !\\ • .. 1�
::�
.� . е li .",...m•"'"' @ 0 0 . Рис. 6.4. Панель приложе ния l nternet Explorer Создание простой панели приложения Для добавления панели приложения Магазина Windows использует­
ся элемент управления WinJS AppBar. Вот, например, как объявить панель приложения, расположенную сверху: <div id="appBar l" data-win-contro l ="WinJS.UI.AppBar"> <button dat a-win-contro l ="WinJS.UI.AppBarCommand" dat a-win-opt i ons ="{ i d: 'cmdPl ay', label: 'Play' , icon: 'play', tool t ip:'Play Song' } "> </button> Глава 6. Меню и всплывающие элементы <button data-win-control ="WinJS.UI.AppBarCoПШland" dat a-win- opt i ons="{ id: 'cmdPause' , label: 'Pause' , icon: 'pause', tool tip: 'Pause Song' } "> </butt on> </div> Примечание. Элемент AppBar по умолчанию включен в страницу defaul t. html, но закомментирован. На этой панели приложения находятся две кнопки: Play Song (Воспроизведение) и Pause Song (Пауза). С каждой кнопкой ассоци­
ированы метка и значок (рис. 6.5). Рис. 6.5. Простая панель приложения Чтобы создать верхнюю панель приложения (панель навигации), задайте свойство placement, как показано ниже: <div id="appBarl" data-win-cont rol ="WinJS.UI.AppBar" data-win- opt ions="{ placement:'t op' } "> <but ton dat a-win-contro l ="WinJS.UI.AppBarCoПШ\and" data-win-opti ons ="{ i d: 'cmdSave', l abel: 'Save' , icon:'s ave'1 tool tip: 'Save Song Li s t' Элемент управления АррВаг •••••lfD } "> </butt on> </div> По умолчанию свойство placement принимает значение "bot­
tom". Если присвоить ему значение "top", то панель окажется сверху (рис. 6.6). Рис. 6.6. Создание двух панелей приложения: верхней и нижней Команды панели приложения На панели приложения могут быть команды четырех типов: кнопка - создается кнопка, выполняющая некоторое дейст­
вие; переключатель -создается переключатель, меняющий состоя­
ние на противоположное; всплывающий элемент - создается всплывающий элемент, в котором можно, например, показать форму; разделитель - создается разделитель (вертикальная черта) других команд. В листинге 6.5 приведен пример панели приложения, содержащей команды всех четырех типов. 1Ш•••1 Глава 6. Меню и всплывающие элементы Лис т инг 6.5. Различные типы команд на панели приложения <!- - Элемент управления AppBar - - > <div id="appBarl" data-win-control ="WinJS.UI.AppBar"> <!- - Команды AppBar - - > <but ton dat a-win-control ="WinJS.UI.AppBarCommand" data-win-opt i ons ="{ i d: 'cmdPlay', l abel: 'Pl ay' , icon: 'pl ay', tool tip:'Pl ay Song' } "> </button> <button data-win-control ="WinJS.UI.AppBarCommand" data-win-opt i ons ="{ i d: 'cmdMut e', type: 'toggle' , l abel: 'Mut e', i con: 'mut e', t ool t ip:'Mute Song' } "> </butt on> <hr dat a-win-control ="WinJS.UI.AppBarCommand" data-win-opt ions ="{ type: 'separator' } " /> <button data-win-control ="WinJS.UI.AppBarCommand" dat a-win-opt ions ="{ i d: 'cmdAddSong' , type: 'f lyout' , l abel: 'Add', icon: 'add', t ool t ip: 'Add Song', f lyout: select ('#f lyAddSong') }"> </button> </div> <!- - Элемент Flyout для добавления песни - - > <div id="f lyAddSong" data-win- control ="WinJS.UI.F lyout"> < form> <input id="inpNewSong" required /> <input type="submi t" value="Add" /> </form> </div> Элемент управления AppBar Если провести пальцем снизу вверх или щелкнуть правой кнопкой мыши, то появится панель приложения, изображенная на рис. 6. 7. Рис. 6. 7. Панель приложения, содержащая команды разных типов Чаще всего на панели приложения встречаются кнопки. Чтобы от­
реагировать на нажатие кнопки, нужно создать обработчик события. Ниже приведен пример обработчика нажатия на кнопку cmdPlay: ( funct i on ( ) { "use stri ct"; funct i on init i al i z e ( ) WinJS. UI .processAll ( ). done ( funct i on ( ) { var cmdPl ay = document.getElementByid ("cmdPl ay"); cmdPlay. addEventLi s t ener ( "c l i ck", funct ion ( ) { cons ole.l og ("Pl ay s ong"); } ) ; } ) ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e ); } ) ( ); Для кнопки можно задать свойства label и icon, определяющие ее вид на панели приложения. Свойство icon может указывать на ка­
кое-нибудь изображение в формате PNG или на один из встроенных значков (всего их больше сотни). Примечание. Полный список встроенных значков имеется в библиотеке Wi nJS ur . j s. Откройте файл u I . j s в окне обозревателя решения, раскрыв папку References (Ссылки), и поищите слово «gl yphs». Переключатель визуально выглядит как кнопка. Но при щелчке по переключателю его цвет изменяется, индицируя состояние. Ниже показано, как с помощью свойства selected узнать, вклю­
чен переключатель или выключен. ( funct i on ( ) { "use stri ct"; funct i on ini t i al i z e ( ) Глава 6. Меню и всплывающие элементы WinJS.UI.proces sAl l ( ) .done ( funct i on ( ) { var cmdМute = document.getElementByid ("cmdМut e"); cmdMute. addEventLi s t ener ( "c l i ck", func t i on ( ) { consol e.l og ( cmdMut e.winControl.selected); } ) ; } ) ; document.addEventLi s tener ("DOMContentLoaded", initial i z e ); } ) ( ); Разделитель визуально представляется вертикальной чертой, от­
деляющей одну группу команд от другой. Для создания разделителя употребляется элемент HR, а не BUTTON. Наконец, команда «вспльшающий элемент�> создает элемент уп­
равления Flyout. В листинге 6.5 команда cmdAdd выводит элемент Flyout, содержащий форму для ввода названия новой песни. Показ контекстно-зависимых команд Некоторые команды на панели приложения относятся к приложению в целом, другие - только к выбранному элементу. Например, кноп­
ка «добавить�>, наверное, должна присутствовать всегда, но кнопки «Удалить�> и «Изменить�> - только в случае, когда выбран какой-то элемент. На панели приложения есть две встроенные секции с именами «global i> и «selectioni>. По умолчанию добавляемые на панель прило­
жения команды попадают в секцию global и располагаются справа. Но можно также добавлять команды в секцию selection - они будут располагаться слева. Команды в секции selection должны быть видны, только когда на странице что-то выбрано. Допустим, нужно показать список задач в списке Lis tView. У пользователя должна быть возможность добавлять новые и удалять существующие задачи. В таком случае кнопку Add (Добавить) имеет смысл поместить в секцию global на панели приложения, а кнопку Delete ( Удалить) - в секцию selection (рис. 6.8). Ниже показано, как описать панель приложения, содержащую ко­
манды Add:и Delete: cdiv id="appBarl" data-win- control ="WinJS.UI.AppBar"> Элемент управления АррВаг ·�••••IШ <button dat a-win-cont ro l ="WinJS.UI.AppBarCommand" dat a-win- opt i ons ="{ i d: 'cmdAdd', 1 аЬе1 : ' Add ' , i con: 'add', tool tip: 'Add Task', type: 'f lyout', f lyout: sel ect ('#f lyAdd'), sect ion: 'global' } "> </butt on> <but ton data-win- contro l ="WinJS.UI.AppBarCommand" dat a-win-opt ions="{ i d: 'cmdDelete', l abel: 'Delete' , i con: 'del ete' , t oo l t ip:'Delete Task', s ect i on: 'select i on', extraCl as s:'appBarSel ect i on' } "> </button> </div> Рис. 6.8. Секции global и selection на панели приложения Отметим, что команда Add помещена в секцию global. Можно было бы и не указывать это явно, потому что секция global подразумевается по умолчанию. Команда Delete помещена в секцию selection. Кроме того, с ней ассоциирован дополнительный СSS-класс appBarSelection. Чуть Глава 6. Меню и всплывающие элементы ниже я покажу, как им воспользоваться, чтобы скрыть или показать команду Delete. Следующий JavaScI"ipt-кoд служит для сокрытия и показа команд на панели приложения: 1 1 По умолчанию команды в секции sel ect i on должны быть скрыты appBarl.hideCoпun ands ( docurnent.querySelectorAll ( '.appBarSelection' ) ); 1 1 При выборе элемента в списке Li stview показать секцию selection lvTasks.addEventLi s tener ("s el ecti onchanged", funct ion ( ) { i f ( lvTasks.select ion.c ount ( ) == 1 ) { appBarl.showCommands ( docurnent.querySelectorAl l (' .appBarSelection') ); appBarl. show ( ) ; } ; } ) ; el s e { appBarl.hideCoпunands ( docurnent.querySelectorAll (' .appBarSelection') ); Сразу после запуска приложения мы хотим скрыть команду Delete, потому что еще ничего не выбрано. В приведенном выше фрагменте метод hideCommands ( ) скрывает все команды, ассоцииро­
ванные с СSS-классом appBarSelection. Далее следует обработчик события selectionchanged, генериру­
емого элементом управления Li s tView. Когда в списке выбран эле­
мент (в результате проведения пальцем или щелчка правой кнопкой мыши), вызывается метод showcommands ( ), который показывает все команды в секции selectio11. Поскольку по умолчанию панель приложения скрыта, то коман­
ду Delete можно и не заметить. Поэтому наш код вызывает метод show ( ), чтобы принудительно показать панель приложения. Таким образом, после выбора элемента в списке Li s tView автоматически появляется панель приложения, и пользователь знает, что есть воз­
можность удалить элемент. Примечание. Можно сделать так, что панель приложения будет видна всег­
да. Для этого достаточно воспользоваться ее свойством sticky. Если это свойство равно true, то панель приложения автоматически не убирается. З адание нас трое к приложения В приложениях Магазина Windo\.vs имеется стандартное место, где располагаются настройки, а именно чудо-кнопка Settiпgs на панели чудо-кнопок, которую можно открыть следующими способами: Задание настроек приложения �··• • ШJ подвести мышь к правому нижнему углу экрана; провести пальцем от правого края экрана к центру; нажать клавиши Windows+i. По умолчанию системные настройки - например, параметры сети и звука - располагаются в нижней части окна Settings. Настройки прав доступа также появляются автоматически. Наконец, если вы приобрели приложение в Магазине Windows, то в окне Settings будут еще присутствовать настройки Rate ( Оценить) и Review ( Рецензи­
ровать). Рис. 6.9. Настройки, присутствующие по умолчанию В окно Settings можно добавить и специальные настройки ваше­
го приложения, например предпочтения пользователя. Кроме того, в число нестандартных настроек может входить информация о прило­
жении, в частности, страницы «Справка» и «0 программе». Нестандартные настройки создаются с помощью элемента управ­
ления Settings Flyout. Для каждой секции настроек вы должны со­
здать от дельный НТМL-файл, содержащий элемент se t t ing s F 1 you t. Ниже я покажу, как это делается, на примере страницы «0 програм­
ме» и персональных настроек. Создание страницы «О программе» В большинстве приложений имеется страница «0 программе», на которой приводятся сведения о компании-разработчике. Это чисто информационная страница (рис. 6.1 0). Глава 6. Меню и всплывающие элементы ihis арр \'ias created Ьу the skill ed programrners of-Acme lncorp::irated. Рис. б. 1 О. Создание страницы «О программе» в окне настроек Чтобы создать страницу «0 программе1>, нужно подготовить НТМL-файл, содержащий элемент управления SettingsFlyout. В листинге 6.6 показано, как может выглядеть такой файл. Лис тинг 6.6. Страница «О программе» <!DOCTYPE html> <html > <head> <t i t l e> About </t i t l e> </head> <body> <div id="divAbout" data-win-control ="WinJS.UI.Sett ingsFlyout" dat a-win-opt i ons ="{ width: 'narrow' } "> <div cl as s ="win-header" s tyl e=" background-color : #4 6 4 6 4 6"> <button oncl i ck="WinJS.UI.Sett ingsFlyout.show ( )" c l as s ="win-backbut t on"></butt on> <div cl as s ="win- l abel">About</div> </div> <div cl as s ="win-content"> Thi s арр was created Ьу the s ki l led programmers o f Acme Incorporated. </div> </div> </body> </html> Задание настроек приложения ••• ••ШJ На странице в листинге 6.6 есть всего один элемент управления WinJS Settings Flyout. Отметим, что для этого элемента задана ширина (свойство width) «naiтow» (узкий). Если открыть такую «узкую» настройку, то она займет столько же места, сколько само окно Settings. Можно задать свойство width равным «wide» (широ­
кий) - тогда для отображения настроек будет отведено больше мес­
та. В элементе Flyout может находиться произвольное содержимое, оно появится при открытии настройки. Однако стоит прислушаться к рекомендациям Майкрософт. Внутри элемента Settings Flyout в листинге 6.6 имеется кнопка возврата в окно Settings. Кроме того, для этого элемента заданы стандартные СSS-классы WinJS: win-label и win-content. Прежде чем можно будет воспользоваться окном «0 програм­
ме», настройку необходимо зарегистрировать, обработав событие settings объекта WinJS .Appl ication: ( funct ion ( ) { "use stri ct"; WinJS.Appl i cati on.ons ett ings = funct i on ( е ) { e.detai l.appl i cationcommands = { } "divAbout": { hre f: "about Sett ings.html", t i t l e: "About" } } ; WinJS.UI.Set t ingsFlyout.populateSett ings ( e ); WinJS.Appl i cati on.s tart ( ); } ) ( ); Сведения о настройках записываются в объект е. detai l s. applicationcommands. В данном случае это путь к НТМL-файлу и название настройки «0 программе». Предупреждение. Если вы создаете свои настройки, то не забудьте вы­
звать метод WinJS. Application. s tart ( ),иначе событие settings не бу­
дет сгенерировано. Создание персональных настроек Пусть требуется дать пользователям возможностям ввести фамилию и имя в настройках приложения Магазина Windows (рис. 6.1 1 ). Мы ведь пишем дружелюбное приложение и хотели бы обращаться к пользователю по имени. Глава 6. Меню и всплывающие элементы Рис. 6. 1 1. Ввод персональных настроек НТМL-страница из листинга 6. 7 - она называется peisonalSetti11gs. html - содержит элемент Settings Flyout, внутри которого находит­
ся НТМL-форма. В форме есть два обязательных поля inpFi rs tName и inpLas tName. Ли сти нг 6. 7. Эле мент Sett i ngsFl y out для пер сона льн ых настроек <!DOCTYPE html > <html> <head> <t i t le> Securi ty </t i t l e> <script type="text/j avas cript" src ="personalSett ings.j s"> '+</s cript> </head> <body> <div id="divPersonal" data-win-control ="WinJS.UI.Sett ingsFlyout • data-win-opti ons ="( width: 'narrow' }"> <div c l as s ="win-header" style=" background-color : #4 6 4 6 4 6"> <button onc l i ck="WinJS.UI.Sett ingsFlyout.show ( )" c l as s ="win-backbutton"></button> <div cl as s ="win- l abe l"> Personal </div> </div> <div c l as s ="win-content"> < form id="frmPersonal"> <div> <l abel> Fi rst Name: <br /> <input id="inpFi rs tName • required /> <!l abel> </div> Задание настроек приложения •••••lllВ <div> <l abel> Last Name: <br /> <i nput id="inpLastName" required /> <!l abel> </div> <div> <input type="submi t" value ="Save" /> </div> </form> </div> </div> </body> </html> Отметим, что страница из листинга 6.7 ссылается на Java­
Script-фaйл personal Settings.js. Соответствующий код приведен в листинге 6.8. Он отвечает за загрузку и сохранение фамилии и имени пользователя. Листинг 6.8. Загрузка и сохранение персональных настроек ( funct i on ( } { 'use s tri ct'; WinJS.UI.Pages.de f ine ("personalSett ings.html", { proces sed: func t i on ( el ement, opt i ons } var roamingSett ings = Windows.St orage.Appl icat i onDat a.current.roamingSett ings; var divPersonal '+ winControl; document.getEl ementByid ("divPersonal"}. } } ) ; } ( ) ); var frmSecuri ty document.getElementByid ("frmPers onal"}; var inpFirstName = document.getElementByid ("inpFirs tName"); var inpLas tName = document.getElementByi d ("inpLas tName"); // Прочитать имя и фамилию divPersonal. addEventLi s t ener ( "beforeshow", func t i on ( } inpFirstName.value = roamingSettings.values ["firstName"] 1 1 inpLastName. value = roamingSettings. values ["lastName"] 1 1 } ) ; //Сохранить имя и фамилию frmSecuri ty.addEventLi s t ener ("submit", funct i on ( ) roamingSett ings.values ["f irs tName"] = inpFirs tName.value; roamingSett ings.values ["lastName"] = inpLas tName.value; } ) ; Глава 6. Меню и всплывающие элементы В листинге 6.8 показан код обработчика события beforeshow элемента Settings Flyout. Это событие возникает непосредственно перед отображением Settings Flyout. В ответ обработчик загружает имя и фамилию пользователя из объекта roamingSet tings и записы­
вает их в поля формы. В листинге 6.8 приведен также обработчик события отправ­
ки формы. Он сохраняет имя и фамилию пользователя в объекте roamingSettings. Они записываются на диск, поэтому будут до­
ступны в следующий раз, когда пользователь откроет приложение. Поскольку настройки сохраняются в перемещаемой области, то до­
ступны при входе с любого компьютера. Примечание. Перемещаемые настройки хранятся в реестре компьютера и синхронизируются между различными устройствами. В настоящее время размер перемещаемой области ограничен 1 00 КБ ( узнать ограничение мож­
но, опросив свойство ApplicationDate. RoamingStorageQuota). Чтобы воспользоваться персональными настройками на стра­
нице, необходимо зарегистрировать их в ассоциированном с ней JavaScгipt-фaйлe. В примере ниже показано, как зарегистрировать страницу �о программе» (из предыдущего раздела) и персональные настройки. ( funct ion ( ) { "use stri ct"; WinJS.Appl ication.onsett ings = func t i on ( е ) { e.detai l.appl i cati oncommands = { } ; "divPersonal" : { href: "personalSettings.html", title: "Personal" } , "divAbout" : { href: "aboutSettings.html", t i t le: "About" } WinJS.UI.Sett ingsFlyout.populateSett ings ( e ); WinJS.Appl i cat i on.s tart ( ); } ) ( ); От ображение диалоговых окон Wi ndows Иногда ничто не может сравниться со старым добрым модальным диалогом. Модальное диалоговое окно не дает взаимодействовать с программой, пока пользователь не даст ответ. Модальные диалоги необходимы в случае, когда приложение не может продолжать работу, не получив определенную информацию. Отображение диалоговых окон Wi ndows Например, приложение, показывающее прогноз погоды, ничего не может сделать, пока не узнает, для какого места этот прогноз показы­
вать. Поэтому при первом запуске имеет смысл спросить у пользова­
теля, какое место его интересует. Модальные окна можно использовать и в ситуациях, когда обыч­
но применяется функцияjаvаSсгiрt alert ( ):чтобы недвусмысленно привлечь внимание пользователя к критически важному предупреж­
дению или информационному сообщению. Примечание. Функция alert ( ) не работает в приложениях Магазина Wi ndows, написанных на JavaScri pt. Поскольку, на мой взгляд, такое опове­
щение - близкий родственник ныне забытого тега HTML <Ыink>, то я счи­
таю, что это хорошо. Для создания модальных диалогов служит класс Mes s ageDialog. Ниже показано, как открыть диалог, содержащий сообщение «Did you lшow that уоuг fly is unzipped?,,, (А вы знаете, что у вас молния расстегнута?) (рис. 6.1 2). var mes s age = new Windows.UI.Popups.Mes s ageDialog ( "Did you know that your f ly i s unz ipped?" ) ; mes s age.showAsync ( ); Рис. 6. 1 2. Диалоговое окно с сообщением Внешний вид диалогового окна можно изменить, добавив коман­
ды. Вот, например, как создать диалог Да/Нет: 1 1 Создаем диалог var mes s age = new Windows.UI.Popups.Mes sageDial og ( "Di d you know that your f ly i s unz ipped?", 11 Warning ! ! ! " Глава 6. Меню и всплывающие элементы ) ; 1 1 Добавляем команды mes s age.commands.append ( new Windows.UI.Popups.UICommand ("&Yes") ); mes s age.commands.append ( new Windows.UI.Popups.UICommand ("&No") ); 1 1 Показываем диалог mes s age.showAsync ( ) .done ( funct ion ( answer ) { i f ( answer.label = = = "&Yes") { cons ol e.log ("You pi cked yes"); el s e { cons ol e.l og ("You picked no"); } } ) ; Здесь мы добавили в диалоговое окно с сообщением два объекта UICommand с метками Yes и No (рис. 6.1 3). Чтобы узнать, какая кнопка была нажата, можно воспользоваться обещанием, которое возвраща­
ет метод showAs ync ( ). Рис. 6. 1 3. Диалоговое окно Yes/No Примечание. Обратите внимание на метки кнопок: « &Yes» и «&No». Знак & говорит, что следующий за ним символ можно использовать для быстрого нажатия кнопки с помощью клавиатуры. Нажатие Alt+Y приводит к нажатию кнопки Yes, а нажатие Alt+N - к нажатию кнопки N. Если нажать и не отпускать клавишу Al t, то под символами быстрого выбора появятся знаки подчеркивания. Р ез юме В этой главе речь шла о всплывающих элементах, меню, настройках, панелях приложения и диалоговых окнах. Мы начали с рассказа об использовании элемента управления Flyout для создания всплыва­
ющих окон, которые могут содержать любую НТМL-разметку. Резюме 11•••1Ш Затем я объяснил, как использовать элемент управления Menu для создания меню команд. Вы узнали, как включить в меню кнопки, всплывающие элементы, переключатели и разделители. Мы обсудили также панель приложения. Вы узнали, что панель приложения - стандартное место для размещения команд в приложе­
ниях Магазина Windows. Я показал, как использовать две секции на этой панели: global и selection. Мы также поговорили об использовании элемента управления s et t i ngsFl y out для задания собственных настроек приложения Ма­
газина Windows. Вы видели, как сохранять настройки в перемещае­
мой области и загружать их оттуда. Наконец, я показал, как с помощью класса Mes s ageDialog созда­
вать модальные диалоговые окна. В качестве примера мы создали диалог, содержащие предупреждение, и диалог типа Да/Нет. Не расслабляйтесь. В следующей главе мы обсудим самый важ­
ный из имеющихся в библиотеке WinJS элементов управления: Li s tView. rЯАВА 7. Эnеме нт уn рав nени я ListView В этой главе:
• Введение в элемент управления ListView.
• Выбор элементов в списке ListView.
• Сортировка списка ListView.
• Фильтрация списка ListView.
• Группировка списка ListView.
• Переключение представлений с помощью контекст­
ного масштабирования.
• Динамическая замена шаблона ListVievv.
• Постепенная загрузка элементов списка ListView.
Вся эта глава посвящена одному-единственному элементу управ­
ления: списку Li s tView. Это самый ва:жный из всех включенных в библиотеку Wi11JS элементов. Всякий раз как требуется показать список объектов, будь то товары, фильмы, файлы или фотографии, на помощь приходит элемент Li s tView.
В этой главе я объясню, как пользоваться наиболее употребитель­
ными возможностями элемента управления Li s tView: выбором, сор­
тировкой, фильтрацией и группировкой. Я также расскажу о дополнительных возможностях элемента Lis tView, в частности, о том, как с помощью контекстного масшта­
бирования можно порождать различные представления одних и тех же данных. Вы узнаете также, как с помощью постепенной загрузки эффективно отображать в списке Li s tView гигантские наборы дан­
ных. Введение в элемент управления li stView ••• ••ШJ В веде ние в э леме нт управления List Vi ew Начнем с простого. Элемент управления Li s tView можно привязать к любому источнику данных, который реализует интерфейс JаvаSсгiрt I Lis tDataSource. В библиотеке WinJS есть два таких объекта. WinJS. Binding. Lis t - позволяет представить массив J ava­
Scгipt в виде источника данных. WinJS. ur. Storage - позволяет представить множество фай­
лов на диске в виде источника данных. Например, источник WinJS. ur. Storage можно использовать для отображения списка фотографий, хранящихся в библиотеке Pictшes. В этой главе мы будем работать только с источником данных WinJS. Binding. List, потом что он самый гибкий. Создать экземп­
ляр этого класса можно из произвольного мaccивajavaScгipt. Напри­
мер, можно отправить Аj ах-запрос удаленному серверу, получить от него массив товаров, а затем отобразить его в элементе управления Li s tView, воспользовавшись источником данных List. Примечание. Вместо WinJS. Binding. Li s t или WinJS. UI. Storage
можно создать собственный источник данных для элемента Lis tView. Например, это полезно для отображения данных из базы данных l ndexedDB. В следующей главе мы создадим несколько своих источников данных, в том числе для l ndexedDB. Посмотрим, как элемент Li s tView используется совместно с ис­
точником данных WinJS. Binding. Li s t. В листинге 7.1 приведен пример списка Li s tView, в котором отображается набор товаров (рис. 7.1). Apples Prit t' '5 2 Рис. 7. 1. Отображение списка товаров Глава 7. Элемент управления ListView
Лис ти нг 7.1. Привязка к источнику данных List (simple\simple.html )
<!DOCTYPE html> <html > <head> <meta charset ="ut f - 8" /> <t i t l e>Chapter0 5 </t i t le> <!- - Ссылки на WinJS -- > < l ink hre f ="//Microsof t.WinJS.1.0.RC/c s s/ui - dark.cs s" '+re l ="s tylesheet" /> < script src="//Micros of t.WinJS.1.0.RC/j s/base.j s"></script> <s cript src="//Micros of t.WinJS.1.0.RC/j s/ui.j s"></script> <!- - Ссылки на ChapterO S --> < l ink hre f="s impl e.c s s" rel ="s tyl esheet" /> < script src="s impl e.j s"></script> </head> <body> <div id="tmpl Product" dat a-win-control ="WinJS.Binding.Template"> <div> <h2 data-win-Ьind="innerText:name"></h2 > Pri ce: <span data-win-bind="innerText:pri ce"></span> </div> </div> <div id="lvProduct s" data-win- control ="WinJS.UI.Li s tView" data-win-opt i ons ="{ i temTempl at e: sel ect ('#tmpl Product') }"></div> </body> </html > Здесь в элементе Lis tView каждый товар отрисовывается по шаблону. Сам шаблон хранится в элементе управления Template с идентификатором tmplProduct.
Предупреждение. Элемент управления Template должен быть объявлен
на странице раньше элемента Lis tView, иначе возникнет таинственная ошибка, которую очень трудно отлаживать. JavaScl'ipt-кoд в листинге 7.2 содержит массив товаров. Из этого массива создается объект WinJS. Binding. List, который привязы­
вается к элементу Li s tView с помощью свойства i temDataSource.
В результате на странице появляется список товаров, взятых из мac­
cивaJavaScгipt. Введение в элемент управления Li stView ••• ••ШJ Ли сти нг 7.2. Привязка к источнику данных List (simple\simple.js) ( funct i on ( ) { "use stri ct 11; funct ion ini t i al i z e ( )
WinJS.UI.processAl l ( ).done ( funct i on ( ) { 1 1 Получаем ссылку на элемент управления Li s tView var lvProducts = document.getElementByid ("lvProduct s"). '+winControl; // Создаем список товаров var l i s t Product s = new WinJS.Binding.Li s t ( [
{ name: "Bread" , pri ce: 2.2 0 }, { name: "Chee s e" , price: 1.1 9 }, { name: "Milk" , pri ce: 2.3 3 }, { name: "Appl es" , price: 5.2 0 } , { name: "Steak" , price: 1 2.9 9 } ] ) ; //Привязываем список товаров к Li s tView lvProduc t s.i t emDataSource = l i s t Product s.dataSource; } ) ; document.addEvent Li s t ener ("DOMContentLoaded", ini t i al i z e );
} ) ( ); В массиве JavaScгipt можно хранить практически все что угодно: результаты запроса к базе данных, результаты Аjах-запроса, после­
довательность чисел Фибоначчи и т. п. Поэтому, умея привязывать Li s tView к объекту Lis t, вы получаете возможность привязки почти к любому типу данных. Например, в элементе Li s tView из листинга 7.3 отображается спи­
сок статей, полученных из ленты блага. Лис т инг 7.3. Привязка к ленте блага ( Ьlog\Ыog.html) <div id="tmplBlog" dat a-win-control ="WinJS.Binding.Template"> <div> <span data-win-bind="innerHTML: t i t l e.t ext WinJS.Binding. 4 oneTime 11 > </span> </div> </div> <div id="lvBlog" data-win-contro l ="WinJS.UI.Li stView" dat a-win- opt i ons="{ Глава 7. Элемент управления ListView
i temTemplate: select ('#tmplBlog') } "> </div> Список статей получен из RSS-ленты по адресу http:// StephenWaltheг.com (кстати, это мой блог) с помощью класса WinRT SyndicationCli ent. В листинге 7.4 приведен код, который читает ленту и привязывает результат к элементу управления Li s tView.
Ли ст инг 7.4. Привязка к ленте блога (Ьlog\Ьlog.html) ( funct i on ( ) { 1'use stri ct"; funct i on ini t i al i z e ( )
WinJS. UI .proces sAl l ( ). done ( funct i on ( ) { // Получаем ссылку на элемент управления Li s tView var lvBl og = document.getElementByid ("lvBl og") .winControl; // Читаем ленту блога с помощью класса WinRT Syndicati onCli ent var c l ient = new Windows.Web.Syndication.Syndicat ionCl i ent ( ); var feedURI = new Windows.Foundation.Uri ("http://stephenwalther. '+com/Ыog / f eed") ; c l i ent.retri eveFeedAsync ( feedURI ) .done ( funct i on ( feed) { 1 1 Преобразуем заголовки статей в обьект Li s t var l i s t i t ems = new WinJS.Binding.Li s t ( feed.i tems ); //Привязываем список lvBlog.i temDat aSource } ) ; } ) ; к Li s tView l i s t i tems.dataSource; document.addEventLi stener ("DOMContentLoaded", ini t i al i z e );
} ) ( ); Объект Syndi cationClient возвращает экземпляр класса Syn­
dica tionFeed, в котором имеется свойство i tems для получения коллекции статей в благе. Эта коллекция передается конструктору класса WinJS.Binding. Li s t, после чего объект Li s t привязывается к элементу управления Li s tView. На рис. 7.2 показано, как выглядят заголовки статей в блоге в элементе Li s tView.
Предупреждение. По умолчанию элемент Li s tView пытается превратить
все отображаемые в нем объекты в наблюдаемые, вызывая метод WinJS. Binding. as ( ). К сожалению, для экземпляров классов Wi п RТ, в частности SyndicationClient, это не работает. Попытка привязать список объектов Wi nRT к элементу Lis tView возбуждает исключение. Введение в элемент управления listView lll••DD Решить эту проблему можно двумя способами. Внимательно взглянув на шаблон в листинге 7.3, вы заметите, что заголовок статьи привязывается с помощью конвертера привязки WinJS. Binding. оnет ime ( ) . В таком случае элемент Lis tView не пытается преобразовать привязываемый объект в на­
блюдаемый. Другое решение - скопировать массив объектов Wi nRT в новый массив объ­
ектов JavaScri pt перед привязкой к элементу Li s tView. Объекты JavaScri pt, в отличие от объектов Wi nRT, с удовольствием позволяют наблюдать за со­
бой. Рис. 7 .2. Отображение списка стате й в блоге Списковый и сеточный макет Элемент управления Li s tView поддерживает два макета: списковый и сеточный. По умолчанию элементы размещаются в ячейках сетки, то есть в несколько столбцов. Если элементов слишком много, то для просмотра не поместившихся нужно прокрутить элемент вправо (рис. 7.3). Примечание. Реализация сеточного макета следует рекомендации WЗС CSS 3 Gri d Layout ( http://dev.wЗ.org/csswg/cssЗ-gri d- layout/). Рис. 7 .3. Сеточный макет Li s tView Глава 7. Элемент управления listView
При использовании сеточного макета можно задать свойство maxRows, которое определяет максимальное число строк сетки. В при­
мере ниже элемент Li s tView настроен для показа сетки с одной стро­
кой (рис. 7.4): <div id="lvProduct s" data-win-contro l ="WinJS.UI.Li s tVi ew• data-win-opt i ons="{
i temTemplat e: select ('#tmpl Product'),
l ayout: { type:WinJS.UI.GridLayout, maxRows:l } } "> </div> Рис. 7.4. Задание свойства maxRows для сеточного макета Помимо сеточного, элемент Li s tView поддерживает списковый макет. В этом режиме список растет в вертикальном направлении (рис. 7.5). Ниже показано, как объявить элемент Li s tView в списковом ре­
жиме: cdiv id="lvProduct s • data-win-control ="WinJS.UI.Li s tView• data-win-opt i ons ="{ i t emTemplate: select ('#tmpl Product'},
l ayout: { type:WinJS.UI.Li s tLayout } }"> </div> Введение в элемент управления Li stVi ew 111•• ЕШ11 Рис. 7.5. Списковый макет Lis tView На самом деле, в списковом макете не используются 1-IТМL­
элементы UL и LI - все элементы списка отрисовываются с помощью DIV'oв. Предотвращение перекрытия элементов списка ListView Результат отображения элемента управления Li s tView в виде сет­
ки может разочаровать, если не понимать, как Li s tView отображает каждый элемент списка. Взгляните на элемент Li s tView, изображенный на рис. 7.6. В нем 7 элементов: АА вввввввв се DD ЕЕ FF GG Однако элемент ВВВВВВВВ перекрывает элемент FF в следую­
щем столбце. Слишком широкая строка портит все дело и получается совсем некрасиво. ЕШ•••I� Глава 7. Элемент управления ListView
Рис. 7.6. Перекрывающиеся элементы в списке Li s tView Если поместить ВВВВВВВВ в начало списка, то перекрытия не будет (рис. 7.7). Теперь все элементы в первом столбце занимают по ширине столько же места, сколько элемент ВВВВВВВВ. Но это само по себе выглядит странно. вввввв вв � � � � � � Рис. 7. 7. Перекрытия нет, но столбцы Lis tView слишком широкие Проблема в том, что Lis tView вычисляет ширину и высоту пер­
вого элемента списка и использует ее для позиционирования всех остальных. Поэтому я рекомендую всегда явно задавать ширину и высоту ячеек. В противном случае ячейки могут либо перекрываться, либо оказаться слишком широкими или высокими. А как же задать ширину и высоту явно? Отдельные элементы списка Lis tView оформляются в виде DIV'oв такого вида: <div cl as s ="win- l i s tview"> cdiv cl as s ="win-container• s tyl e="width:l 4 2 px;height:5 4px"> <div cl as s ="win- i t em"> АААА </div> c/div> Введение в элемент управления li stView �l l ••ШI <div c l as s ="win-c ontainer• s tyl e="width:1 4 2px;height:5 4px"> <div cl as s = •win- i tem"> вввв </div> <div cl as s = •win-container" s tyl e="width:1 4 2 px;hei ght:5 4px"> cdiv c l as s = •win- i t em"> сссс </div> </div> . . . и так далее ...
</div> Для простоты я опустил несколько уровней вложенных DIV'oв. Но основная идея ясна - каждый элемент списка Li s tView представ­
лен элементом DIV с классом win-container, который содержит элемент DIV с классом win- i tem.
Отметим, что для каждого DIV'a с классом win-container задан еще атрибут s tyle, в котором явно указана ширина и высота. У всех таких DIV'oв ширина и высота одинаковы. Lis tView вычисляет их на основе размера первого элемента списка. Поскольку атрибут s tyle имеет более высокий приоритет, чем ат­
рибут clas s, высота и ширина, указанные в классе win-container, игнорируются - даже не пытайтесь их устанавливать. В сеточном макете можно было бы явно задать ширину и высоту каждого элемента списка в классе win- item. Однако использование класса win-item в списковом макете влечет за собой другие пробле­
мы. Обратите внимание, что на рис. 7.8 фон ячейки закрашен серым цветом не полностью. Рис. 7.8. Ширина серого фона, применяемого для выделения элемента, недостат очна Я рекомендую следующее решение, которое работает как в се­
точном, так и в списковом режиме. Указывайте в шаблоне для одно­
го элемента списка Li s tView DIV с подходящим СSS-классом. Вот, например, как может выглядеть шаблон, содержащий DIV с классом product i tem для представления товара: <div id="tmplProduct" data-win-control ="Wi nJS.Binding.Templ at e"> Глава 7. Элемент управления ListView
<div c l as s ="produc t i t em"> <h2 dat a-win-bind="innerText:name"></h2> </div> </div> При использовании сеточного макета задавайте в классе producti tem ширину и высоту явно. Например, в результате приме­
нения показанного ниже класса producti tem к элементу управления Li s tView с идентификатором lvProducts каждая ячейка списка бу­
дет иметь размер SOxSO пикселей, с рамкой и отступами. #lvProduct s.product i t em width: 5 0рх; height: 5 0рх; border: s ol i d lpx whi t e; padding: l Opx; На рис. 7.9 показан результат использования класса product i tem.
Все ячейки имеют одинаковую ширину и высоту. Рис. 7.9. Явное задание ширины и высоты Обратите внимание, что первый элемент - ВВВВВВВВ - обрезан по ширине. В применяемых по умолчанию таблицах стилей ui -daгk.css и ui-light.css установлен стиль overflow: hidden. Это хорошо, потому что иначе некоторые элементы DIV выглядели бы шире или выше других, что некрасиво. При использовании спискового макета я также рекомендую вклю­
чать в шаблон контейнерный элеl\Iент DIV. Но n отличие от сеточного, явно задавать ширину или высоту не следует. Пусть яl1 ейки занимают всю ширину элемента Lis tView.
Выбор элементов в списке ListView
В результате применения следующих СSS-классов в списковом режиме каждая ячейка будет иметь рамку и отступы, как показано на рис. 7.1 0. #lvProduct s .product i t em { border: s ol i d lpx whi t e; padding: l Opx; #lvProduc t s.win- l i s tview width: З О Орх; Предупреждение. Между# 1 vProducts и . producti tem стоит пробел ( пос­
кольку это дочерний селектор), но в строке #1 vProducts. win- l i s tview пробела нет ( потому что этот селектор дочерним не является). Рис. 7.1 0. Сnисковый макет элемента Li s tView Выб ор э леме нтов в списке ListVi ew В элемент управления Li s tView встроена поддержка выбора элемен­
тов. По умолчанию для выбора элемента нужно щелкнуть по нему правой кнопкой мыши или провести пальцем. К выбору элементов относятся три события Li s tView:
i teminvoked - возникает при касании элемента списка ListView пальцем; selectionchanging - возникает перед изменением выбранно­
го в данный момент элемента; selectionchanged - возникает после изменения выбранного элемента. ЕШ•• · · � Глава 7. Элемент управления listView
Активация (invoking) элемента - не то же самое, что выбор. При выборе элемента списка генерируется событие selectionchanged и рядом с элементом рисуется галочка. При активации элемента гене­
рируется событие i teminvoked, но элемент не выбирается. Это собы­
тие используется для действий, отличных от выбора. У элемента управления Lis tView существует несколько свойств, относящихся к выбору. selection - возвращает объект WinJS. UI. I S election, у ко­
торого имеются методы для установки и получения выбран­
ных элементов списка L i s t Vi ew.
selectionМode - прочитать или установить режим выбора. Возможные значения: 'none', 's ingle', 'multi'.По умолча­
нию подразумевается значение 'mul ti'. swipeBehavior - прочитать или установить значение, опреде­
ляющее, что происходит, когда пользователь проводит паль­
цем по элементу списка (жест смахивания). Возможные зна­
чения: 'se lect' и 'none'. tapBehavior - прочитать или установить значение, определя­
ющее, что происходит, когда пользователь касается элемента списка пальцем или щелкает по нему мышью. Возможные зна­
чения: 'di rectSelect', 'toggleSelect', 'invokeOnl y' или 'none'. Названия возможных значений свойства tapBehavior могут ввести в заблуждение. В зависимости от значения tapBehavior ге­
нерируются различные комбинации событий selectionchanged и iteminvoked (cм. табл. 7.1 ). Табл ица 7.1. Резул ьтат уста новки свойства tapBehavior selectionchanged iteminvoked 'directSelect' да да 'toggleSelect' да да 'invokeOnly' нет да 'попе' нет нет Обратите внимание, что в обоих режимах 'directSelect' и 'toggleSelect' генерируются события selectionchanged и iteminvoked. Разница в том, что в режиме 'toggleSelect' со­
стояние выбора изменяется на противоположное, а в режиме 'directSelect' - нет. Выбор элементов в списке ListVi ew Создание представления «основной/подробности» ••• ••ШJ
Пусть требуется создать представление «основной/подробности», со­
держащее два элемента управления Lis tView. В основном Lis tView
будут отображаться категории товаров, а в подробном - сведения о самих товарах (рис. 7.1 1 ). Рис. 7. 1 1. Предста вление «основной/подробности» В листинге 7.5 показаны необходимые элементы управления Template и Lis tView.
Л ис т инг 7 .5. Создание формы «основной/подробности» ( masterDetail\masterDetail. html ) <!- - Шаблоны - - > <div id="tmplCat egory" dat a-win- control ="WinJS.Binding.Templat e"> <div c l as s ="categoryi tem"> <h2 data-win-Ьind="innerText:cat egoryName"></h2 > </div> </div> <div id="tmpl Product" dat a-win- contro l ="WinJS.Binding.Template"> <div c l as s ="produc t l t em"> <h2 dat a-win-bind="innerText:productName"></h2 > Pri ce: < span data-win-bind="innerText:pri ce"></span> </div> </div> <div id="container"> <!- - Основной Li s tVi ew - категории - - > <div id="lvCategori e s" dat a-win-contro l ="WinJS.UI.Li stView" data-win-opt i ons="{ Глава 7. Элемент управления ListView i t emTemplate: select ('#tmplCategory'),
s el ecti onMode: 's ingl e', t apBehavior: 'directSelect' , swipeBehavior: 'sel ect', l ayout: { type: WinJS.UI.Li stLayout } } "> </div> <!- - Подробный Li s tView - товары - - > <div i d="lvProduct s" data-win-contro l ="WinJS.UI.Li s tView" dat a-win-opt i ons ="{ i temTemplate: select ('#tmpl Product'),
s el ecti onMode: 'none', l ayout: { type: WinJS.UI.Li stLayout } }"> </div> </div> Отметим, что в Li s tView с категориями (его идентификатор lvCategories ) установлены свойства selectionMode, tapBehavior
и swipeBehavior. Выбирать категории можно как касанием, так и смахиванием. В листинге приведенjаvаSсгiрt-код для показа товаров, входящих в выбранную категорию. Лис тинг 7.6. Создание формы «основной/подробности» (masterDetail\masterDet ail.js) ( funct i on ( )
"use s tri ct";
funct i on ini t i al i z e ( )
WinJS.UI.processAl l ( ) .done ( funct i on ( )
//Получить ссылки на элементы управления Li s tView var lvProduct s = document.getElementByi d ("lvProduct s"). '+winControl; var lvCategori es = document.getElementByid ("lvCategori es"). '+winControl; // Создать массив категорий и товаров var product s = [ { categoryName: "Beverages",
produc t s: [ { productName: "Peps i", price: 4.0 0 }, { productName: "Mi lk", price: 2. 1 1 } , { productName: "Moxi e", price: 1. 3 3 } Выбор элементов в списке ListView
} ' { categoryName: "Meat", produc t s: [ 111 ••1m.1 { productName: "Steak", price: 3 4.3 3 },
{ productName: "Chicken", pric e: 2.0 1 } ] } ' { cat egoryName: "Frui t" , produc t s: [ { productName: "Apples" , pr i c e: 2 . 8 8 } , { productName: "Oranges", price: 7. 0 1 } } ] ; 1 1 Создать список Li s t категорий и товаров var l i s t Product s = new WinJS.Binding.Li s t ( product s );
1 1 Привяз ать список к элементу Li s tView Categori es lvCategori es.i t emDataSource = l i s t Produc t s.dataSource; 1 1 Обработать событие s el ect ionchanged 1 vCategories. addEventListener ( "selectionchanged", funct ion ( )
i f ( 1 vCat egori es. s el ecti on. count ( ) > О ) { lvCategories.selection.geti tems ( ) .done ( funct i on ( items ) 1 1 Получить товары в первой выбранной категории var selectedProduct s = i t ems [ O J .data.produc t s; 1 1 Преобразовать в список var l i stSel ectedProduct s '+Li s t ( s el ectedProduct s ); new WinJS.Binding. 1 1 Привязать список к элементу Li stView Products lvProduc t s.i t emDataSource = l i stSel ectedProduc t s. '+dataSource; } } ) ; } ) ; } ) ; document.addEventLi s tener ("DOMContentLoaded", init i al i z e );
} ) ( ) ; Категории и входящие в них товары хранятся в одном массиве JavaScгipt под названием products. Этот массив преобразуется в объект WinJS. Binding. Li s t, который затем привязывается к эле­
менту управления Li s tView для показа категорий. Глава 7. Элемент управления ListView
После выбора категории - касанием или смахиванием - элемент Lis tView генерирует событие selectionchanged. В листинге 7.6 приведен код обработчика этого события: // Обработать событие s el ecti onchanged lvCat egori es.addEventLi s t ener ("s el ect i onchanged", funct i on ( )
i f ( lvCategories.s el ecti on.count ( ) > 0 ) { lvCategori es.sel ecti on.get i tems ( ) .done ( funct i on ( i tems ) 1 1 Получить товары в первой выбранной категории var selectedProducts = i tems [ O ] .dat a.produc t s; 1 1 Преобразовать в список var l i stSel ectedProduct s new WinJS.Binding.Li s t '+ ( selectedProduct s ) ; } } ) ; /!Привязать список к элементу Li stView Product s lvProduc t s.i t emDataSource = l i s tSel ectedProduc t s.dataSource; } ) ; Для получения выбранной категории вызывается метод selection. geti tems ( ), который возвращает обещание. В методе done этого обещания извлекается и преобразуется в WinJS. Binding. Li s t массив товаров, принадлежащих выбранной категории. Этот объект Li s t привязывается к элементу управления l vProducts, в ре­
зультате чего список товаров отображается на экране. Выбор нескольких элементов Если свойству selectionMode элемента управления Lis tView при­
своить значение 'mul t i', то можно будет выбирать сразу несколько элементов. Пусть требуется показать список товаров и разрешить покупате­
лю класть в корзину несколько товаров (рис. 7.1 2). В таком случае можно использовать два элемента управления Li s tView: для товаров и для корзины (листинг 7.7). Л исти нг 7. 7. Выбор нескольких элементов (selectMultiple\selectMultiple.html ) <!- - Шаблоны - - > <div i d="tmpl Product • data-win-control ="WinJS.Einding.Templ ate"> <div cl as s = • produc t i t em"> <h2 dat a-win-Ьind="innerText:productName"></h2 > Price: <span dat a-win-bind="innerText:pri ce"></span> </div> Выбор элементов в списке listView </div> <div id="tmplShoppingCart • data-win-control ="WinJS.Binding.Templat e"> <div c l as s ="shoppingCart i t em"> <h2 data-win-Ьind="innerText:productName"></h2 > </div> </div> <div i d="container"> <div id="containerProduct s"> <hl> Product s </hl> <!- - Элемент для показа товаров --> <di v i d="lvProduct s • data-win- control ="WinJS.UI.Li s tView" data-win-opt i ons ="{ i temTemplat e: sel ect ('#tmpl Product'I,
s el ecti onMode: 'mul t i', tapBehavi or: 'toggleSelect' , swipeBehavior: 'sel ect' } "> </div> </div> <div id="containerShoppingCart"> <hl > Shopping Cart </hl> <!- - Корзина - - > <div id="lvShoppingCart • data-win-control ="WinJS.UI.Li s tView" data-win-opti ons ="{ i temTemplat e: sel ect ('#tmpl ShoppingCart'),
s el ecti onMode: 'none', l ayout: { type: WinJS.UI.Li s tLayout } } • > </div> </div> </div> Рис. 7 .1 2. Выбор нескольких элементов в списке Li s tview
Глава 7. Элемент управления ListView
Отметим, что в элементе для показа товаров - Li s tView с иден­
тификатором 1 vProducts - свойству selectionMode присвоено зна­
чение 'mul ti', а свойству tapBehavior - значение 'toggleSelect'. При таком значении tapBehavior касание элемента списка меняет его состояние (выбран/не выбран) на противоположное. В листинге 7.8 показан код копирования выбранных элементов списка товаров в корзину. При выборе элемента генерируется со­
бытие selectionchanged, в ответ на которое выбранные элементы копируются в новый массив JavaScript. Этот массив преобразуется в объект WinJS. Binding. Lis t, который привязывается к элементу управления Li s tView, представляющему корзину. Лис тинг 7 .8. Выбор нескольких элементов (selectMultiple\setectMultipte.js)
( funct i on ( ) { "use stri ct"; funct ion ini t i al i z e ( )
WinJS. UI. proces sAl l ( ) . done ( funct ion ( ) { 1 1 Получаем ссылки на элементы управления Li s tView var lvProducts = document.getElementByi d ("lvProduc t s").
'+winControl; var lvShoppingCart = document.getElementByid '+ ( "1 vShoppingCart") . winControl; 1 1 Создаем список товаров var l i s t Product s = new WinJS.Binding.Li s t ( [
{ productName: "Bread", pri ce: 2.2 0 }, { productName: "Cheese", pri ce: 1.1 9 },
{ productName: "Mi l k", pri ce: 2.3 3 }, { productName: "Appl es", price: 5. 2 О } , { productName: "St eak", price: 1 2.9 9 } ] ) ; 1 1 Привязываем список к элементу управления ListView с товарами lvProduct s.i temDataSource = l i s t Product s.dataSource; 1 1 Обрабатываем событие s el ecti onchanged lvProducts.addEventListener ("selectionchanged", funct i on ( )
lvProduc t s.sel ecti on.get i t ems ( ) .done ( funct i on ( i tems ) { 1 1 Копируем выбранные элементы в новый массив var select edProducts = [ ]; i t ems.forEach ( funct i on ( i t em) selectedProduct s.push ( it em.data ); } ) ; 1 1 Преобразуем выбранные товары в список Li s t var l i s t Product s = new WinJS.Binding.Li s t Сортировка списка ListView
11••1Ш1 '+ ( s el ectedProduct s ) ; 1 1 Привязываем список к корзине lvShoppingCart.it emDataSource l i s t Product s.dataSource; } ) ; } ) ; } ) ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i z e );
} ) ( ); Сорти ровка списка ListVi ew Чтобы отсортировать элементы, отображаемые в списке Lis tView,
нужно отсортировать его источник данных. Например, список това­
ров можно отсортировать по названию или по цене. У объекта WinJS. Binding. Lis t имеется метод createSorted ( ),
который принимает функцию сортировки и возвращает объект отсортированным в соответствии с этой функцией. Например, следующий код сортирует список товаров по назва­
нию: 1 1 Создать список товаров var l i s t Product s = new WinJS.Binding.Li s t ( [
{ name: "Bread", price: 2. 2 0 } , { name: "Chees e •, price: 1.1 9 },
{ name: "Mi l k", price: 2.3 3 }, { name: "Appl e s •, price: 5.2 0 }, { name: "St eak", price: 1 2.9 9 } ] ) ; 1 1 Отсортировать товары var sortedLi s t Product s = l i s t Product s.createSort ed ( func t i on '+( i t eml, i t em2 ) { return i t eml.name > i t em2.name ? 1 : - 1; } ) ; 1 1 Привязать список товаров к элементу управления Li s tView lvProduct s.i t emDataSource = sortedLi s t Product s.dataSource; Здесь мы вызываем метод createSorted ( ),чтобы создать новый отсортированный источник данных из исходного иеотсортированно­
го списка товаров. Затем элемент Lis tView привязывается к новому источнику данных, а не к исходному. В этом примере товары сортируются следующей функцией: Глава 7. Элемент управления ListView
funct i on ( i teml, i t em2 ) return i t eml.name > i t em2.name ? 1 : - 1; Функция сортировки принимает два объекта и возвращает одно из трех значений: О - порядок элементов одинаков; • -1 - первый элемент должен предшествовать второму; 1 - первый элемент должен следовать за вторым. Источник данных s o r t ed Li s t P r od uc t s - «живой:;.. При добав­
лении в l i s tProducts нового элемента он сразу же появляется в ListView в нужном месте с учетом сортировки - без какого-либо вмешательства с вашей стороны. Примечание. Для сортировки методом createSorted ( ) можно ис­
пользовать точно такие же функции сортировки, как для стандартного мето­
да JavaScri pt sort ( ),который сортирует массив. Ф ильтрация спис ка List Vi ew Чтобы отфильтровать элементы, отображаемые в списке Lis tView, нужно отфильтровать его источник данных. Например, можно пре­
доставить пользователю возможность фильтровать большой список товаров, оставив только те, что отвечают фильтру (рис. 7.1 3). Рис. 7 .1 3. Фильтрация списка товаров Отфильтрованный объект WinJS. Binding. Li s t можно создать из существующего методом createFil tered ( ). Этот метод принимает функцию фильтрации, которая должна возвращать t r ue или false в зависимости от того, следует включать элемент в новый список или нет. Фильтрация списка ListView
В листингах 7.9 и 7.1 0 показано, как использовать метод createFi ltered ( ) для создания поисковой формы keyup. Стра­
ница из листинга 7.9 содержит поле ввода и элемент управления Lis tView. Листинг 7.9. Филь трация списка товаров (filter ing\fi ltering.html )
<div> <input id="inputFi l ter" /> </div> <div id="tmpl Product" data-win-control ="WinJS.Binding.Template"> <div c l as s ="product l t em"> <h2 data-win-bind="innerText:name"></h2 > Price: <span data-win-bind="innerтext:pri ce"></span> </div> </div> <div id="lvProduct s" data-win-control ="WinJS.UI.Li s tView" data-win-opt i ons ="{ i temTemplate: s el ect ('#tmpl Product') } "> </div> JavaScгipt-кoд в листинге 7.1 0 фильтрует элемент Li s tView, так что в нем остаются только товары, отвечающие заданному фильтру. Ли стинг 7. 1 О. Фильтрация списка товаров (filtering\filtering.js)
( funct i on ( ) { "use stri ct"; 1 1 Создаем список товаров var l i s t Products = new WinJS.Binding.Li s t ( [ { name: "Bread'', pri ce: 2.2 0 }, { name: "Brocco l i", price: 1.1 9 }, { name: "Bananas", pri ce: 2. 3 3 } , { name: "Apple s", price: 5.2 0 }, { name: "Apple Sauce", price: 1 2.9 9 ] ) ; funct i on ini t i al i z e ( ) { WinJS.UI.proces sAl l ( ).done ( funct i on ( ) { //Получаем ссылки на элементы DOM и элементы управления var lvProduct s = document.getEl ementByid ("lvProduct s"). '+winControl; var inputFi l ter = document.getElementByid ("inputFi l ter");
// Привязываем нефильтрованный список товаров к Li s tView Глава 7. Элемент управления listView
lvProduc t s.i temDataSource l i s t Produc t s.dataSource; input Fi l ter. addEventLi s t ener ( "keyup", funct ion ( } { 1 1 Фильтруем источник данных var f i l teredLi s t Product s = l i s t Produc t s.createFi l t ered '+ ( funct i on ( i tem} return i tem.name.toLowerCase ( ) .indexOf ( inputFilter. '+value ) == О; } ) ; 1 1 Привязываем список товаров к Li s tView lvProduc t s.i temDat aSource = f i l t eredLi s t Produc t s. '+dat aSource; } ) ; } } ; document.addEventLi stener ("DOMContentLoaded", ini t i al i z e );
} ) ( ); Отдельно приведем участок кода, где вызывается метод createFil tered ( ):
1 1 Фильтруем источник данных var f i l t eredLi s t Product s = l i s t Produc t s.createFi l t ered ( funct i on '+ ( item) { return i t em.name.toLowerCas e ( } .indexOf ( input Fi l ter.value ) == О;
} ) ; 1 1 Привязываем список товаров к Li s tVi ew lvProduct s.i temDat aSource = f i l t eredLi s t Produc t s.dataSource; Функция, передаваемая методу createFi l tered ( ), определяет, какие элементы вклю<�ать в отфильтрованный список товаров. Она возвращает true только для тех объектов из источника данных, название которых начинается строкой в поле ввода. Таким образом, если ввести в поле ввода строку «Ьг», то товары << Вгеаd» и «Bгoccol i » будут отвечать фильтру, а «Banaпas» - нет. Группировка списка List Vi ew Элементы в списке Lis tView можно группировать. Например, можно показать не плоский список товаров, а сгруппированный по катего­
риям (рис. 7.1 4). Чтобы воспользоваться этой возможностью, нужно создать сгруппированный источник данных. Для этого служит метод createGrouped ( ) класса WinJS. Binding. List.
Группировка списка ListView
•••••ва Рис. 7.1 4. Группировка списка ListVi ew В листингах 7.1 1 и 7.1 2 показано, как метод createGrouped ( ) ис­
пользуется для группировки товаров по категориям. Ли сти нг 7.1 1. Группировка списка ListView (grouped\grouped.html) <!- - Шаблоны - - > <div id="tmpl ProductGroupHeader" dat a-win-control ="WinJS.Einding. 4Templat e" > cdiv c l as s ="productGroupHeader"> <hl data-win-bind="innerText: t i t l e"></hl> c/div> </div> <div id="tmpl Product" data-win-control ="WinJS.Binding.Template"> <div c l as s ="produc t i t em"> <h2 data-win-bind="innerText:name"></h2 > Pri ce: < span dat a-win-bind="innerText:pri ce"></span> </div> c/div> <!- - Элемент Li s tView с товарами - - > <div id="lvProduct s" data-win-control ="WinJS.UI.Li stView" data-win-opti ons ="{ i t emTemplate: select ('#tmpl Product'),
groupHeaderTemplate: select ('#tmpl ProductGroupHeader') } "> </div> На НТМL-странице из листинга 7 .1 1 находятся два шаблона: для заголовка группы и для элементов группы. Шаблон заголовка груп­
пы декларативно ассоциируется с элементом управления Lis tView
при помощи его свойства groupHeaderTemplate.
Предупреждение. Элемент Li s tView подцерживает группы только в ре­
жиме сеточного макета. Глава 7. Элемент управления ListView
Лист инг 7. 1 2. Группировка списка ListView ( grouped\grouped.js)
( funct i on ( ) "use stri ct"; fun c t i o n ini t i al i z e ( ) { WinJS.UI.processAl l ( ) .done ( funct i on ( )
1 1 Получаем ссылку на элемент управления Li s tView var lvProducts = document.getElementБyid ("lvProduc t s").
'+winControl; 1 1 Создаем список товаров var l i s t Product s = new WinJS.Бinding.Li s t ( [ { name: "Milk", price: 2.44, cat egory: "Бeverages" }, { name: "Orange s", price: 1.9 9, category: "Frui t" }, { name: "Wine", price: 8.5 5, cat egory: "Бeverages" }, { name: "Appl e s", price: 2.4 4, category: "Frui t" }, { name: "Steak", price: 1. 9 9, cat egory: "Other" } , { name: "Eggs", price: 2.44, category: "Other" }, { name: "Mushrooms", price: 1. 9 9, category: "Other" } , { name: "Yogurt", pric e: 2.4 4, cat egory: "Other" }, { name: "Soup", price: 1. 9 9, category: "Other" } , { name: "Cereal", price: 2. 4 4, category: "Other" } , { name: "Peps i", price: 1.9 9, cat egory: "Бeverages" ] ) ; 1 1 Создаем сгруппированный источник данных var groupLi s t Product s = l i s t Produc t s.creat eGrouped ( funct i on ( dataitem) { ) ; return dataitem.category; } ' funct i on ( dataitem) return { t i t l e: dataitem.cat egory };
} ' fun c t i o n ( groupl, group2 ) return groupl > gr oup2 ? 1 - 1; 1 1 Привязываем список товаров к Li s tView lvProduct s.groupDataSource = groupLi s t Produc t s.groups. '+dat aSource; lvProduct s.i temDataSource = groupList Produc t s.dataSource; } ) ; do cument.addEven t L i s t e ner ("DOMCont ent Loa ded", i n i t i a l i z e );
} ) ( ); Код в листинге 7.1 2 создает два источника данных: плоский и сгруппированный. Сгруппированный источник данных создается путем вызова метода createGrouped ( ). Переключение предсrавлений с помощью контексrного... 111 •• IJD Отметим, что метод cr eat e Grouped ( ) принимает в качестве аргу-
ментов три функции. groupKey - ассоциирует каждый элемент списка с группой. Эта функция принимает объект данных и возвращает ключ, представляющий его группу. В примере выше мы возвращаем в качестве ключа свойство товара cat egor y. groupDa ta - возвращает объект, который должен отображать­
ся по шаблону заголовка группы. В примере выше эта функ­
ция возвращает название категории. groupSorter - эта функция определяет порядок следования групп. В примеры выше группы упорядочены по алфавиту: Beveгages, Fгuit, Otheг. Оба источника данных привязываются к элементу управления Li s tView в следующих строчках: 1 1 Привязываем список товаров к Li s tView lvProduct s.groupDat aSource = groupLi s t Produc t s.groups.dataSource; lvProduc t s.i t emDat aSource = groupLi s t Produc t s.dataSource; Сгруппированный источник данных - «живой>->, то есть при его из­
менении изменяются и группы, отображаемые в элементе Li s tView.
Кроме того, сгруппированный источник данных можно использовать вместе с фильтрацией. Пере кл ючение предста вл ений с помощ ью конте кстно го мас шта бирова ния Контекстное масштабирование (Semantic Zoom) - это встроенная в Windows 8 возможность просматривать данные в двух разных масш­
табах. Она используется, например, на начальном экране Windows 8. По умолчанию на начальном экране приложения показаны в круп­
ном масштабе (рис. 7.1 5). Но масштаб можно и уменьшить, «отда­
лив>-> значки приложений (рис. 7.1 6). Если вы работаете с мышью, то для уменьшения масштаба нужно щелкнуть по кнопке - в правом нижнем углу экрана. В случае сен­
сорного интерфейса масштабирование осуществляется сведением и разведением пальцев. Для реализации контекстного масштабирования в приложениях Магазина Windows можно воспользоваться элементом управления ШJ • •• l f'" Глава 7. Элемент управления ListView WinJS SemanticZoom. В применении к элементу Lis tView этот эле­
мент позволяет просматривать данные в двух видах: с высоты 1 00 фу­
тов и с высоты 1 о футов. Рис. 7.15. Начальный экран по умолчанию Рис. 7.16. Начальный экран в более мелком масштабе Допустим, что имеется большой набор товаров, сгруппированных по категориям. Чтобы упростить переход из одной категории в дру­
гую, можно воспользоваться контекстным масштабированием. По умолчанию показываются товары, сгруппированные по категориям (рис. 7.1 7). Если же уменьшить масштаб, то останется только список категорий (рис. 7.1 8). Переключение представлений с помощью контекстного... 111•• 1!111 Beverages Frui Mi lk Pnrf' /44 i Orang
� L
W-i n_e-- - �·---� lj i1щi.:.IO ] i Apples
l 1111ц!. L ·� �epsi J rn,c: 1.9') ·-----------------
Рис. 7. 1 7. Использование контекстного масштабирования ( вид с высоты 1 О футов) Рис. 7 .1 8. Использование контекстного масштабирования ( вид с высоты 1 00 футов) На НТМL-странице из листинга 7.1 3 иллюстрируется реализа­
ция контекстного масштабирования. Страница содержит элемент SemanticZ oom с двумя дочерними элементами Li s tView. Каждому Li s tView соответствует свой масштаб. Листинг 7. 1 3. Контекстное масштабирование ( semaпticZoom\semanticZoom.html) <!- - Шаблон для крупного масштаба - - > <div id="trnpl ProductGroupHeader" data-win-contro l ="WinJS.Binding. '+-Ternplat e"> <div c l as s ="productGroupHeader"> <hl data-win-bind="innerText: t i t l e"></hl > </div> </div> <div id="trnpl Product" ЕШ•• 1 1� Глава 7. Элемент управления ListView
data-win-control ="WinJS.Binding.Template"> <div cl as s ="product i t em"> <h2 data-win-Ьind="innerText:name"></h2 > Price: <span data-win-bind="innerText:pri ce"></span> </div> </div> <!- - Шаблон для мелкого масштаба - - > <div id="tmpl Category" data-win-control ="WinJS.Binding.Template"> <div cl as s ="categoryitem"> <h2 data-win-bind="innerText:t i t l e"></h2 > </div> </div> <!- - Элементы управления Semant i c Zoom и Li s tView - - > <div i d="divSemant i cZoom" data-win-control ="WinJS.UI.
'+Semant iczoom" > <!- - Li s tView для крупного масштаба ( товары) - - > <div id="lvProduct s" dat a-win-control ="WinJS.UI.Li s tVi ew" data-win-opt ions ="{ i temTempl at e: sel ect ('#tmpl Product'I,
groupHeaderTemplate: sel ect ('#tmpl ProductGroupHeader')
} "> </div> <!- - Li s tView для мелкого масштаба ( категории) - - > <div id="lvCategori es" data-win-contro l ="WinJS.UI.Li s tView" data-win-opt i ons ="{ i temTemplate: sel ect ('#tmplCategory')
} "> </div> </div> Элемент SemanticZoom содержит два элемента Li s tView для раз­
ных масштабов и позволяет переключаться с одного на другой. В листинге 7.1 4 приведен код реализации контекстного масштаби­
рования. Источник данных, представляющий список товаров, привя­
зан к элементу Li s tView для I<рупного масштаба. Кроме того, к обоим элементам Li s tView привязан сгруппированный источник данных. Листинг 7 .1 4. Контекстное масштабирование (semanticZoom\semanticZoom.js)
( funct ion ( 1 { "use stri ct";
funct ion ini t i al i z e ( )
Переключение представлений с помощью контекстного... l l l ••ШJ WinJS.UI.processAl l ( ) .done ( funct i on ( )
1 1 Получаем ссылку на элемент управления Li s tView var lvProduct s = document.getElementByid ("lvProduct s") '+winControl; 1 1 Создаем список товаров var l i s t Product s = new WinJS.Binding.Li s t ( [
{ name: "Milk", price: 2.4 4, category: "Beverages" }, { name: "Oranges", price: 1.9 9, cat egory: "Frui t" }, { name: "Wine", price: 8.5 5, category: "Beverages" }, { name: "Appl es", price: 2. 4 4, category: "Frui t" } , { name: "St eak", pri ce: 1.9 9, category: "Other" }, { name: "Eggs", pri ce: 2.4 4, category: "Other" }, { name: "Mushrooms", price: 1.9 9, cat egory: "Other" }, { name: "Yogurt", price: 2.4 4, category: "Other" }, { name: "Soup", pri ce: 1.9 9, category: "Other" }, { name: "Cereal", price: 2.4 4, category: "Other" }, { name: "Peps i", price: 1.9 9, category: "Beverages" ] ) ; 1 1 Создаем сгруппированный источник данных var groupLi s t Product s = l i s t Produc t s.createGrouped ( func t i on ( dataitem) { return dat aitem.cat egory; } ' funct ion ( dataitem) return { t i t l e: datai t em.category };
} ' funct ion ( groupl, group2 ) return groupl > group2 ? 1 - 1; } ) ; 1 1 Привязываем список товаров к элементу Li s tView 1 1 для крупного масштаба lvProduc t s.i t emDataSource = groupLi s t Produc t s.dataSource; lvProduct s.groupDat aSource = groupLi s t Produc t s.groups. '+dataSource; 1 1 Привязываем список категорий к элементу Li s tView 1 1 для мелкого масштаба lvCategori es.it emDat aSource = groupLi s t Product s.groups. '+dataSource; } ) ; document.addEventLi s tener ("DOMContentLoaded", ini t i al i ze ); } ) ( ); Глава 7. Элемент управления ListView
Динамиче с кая за мена шаблона List Vi ew Свойству i t e mTempl at e элемента управления L i s t Vi ew можно при­
своить функцию. А это означает, что шаблон, по которому отрисовы­
ваются элементы списка, отображаемого в L i s t Vi ew, можно менять во время выполнения. Пусть, например, требуется отображать товары по двум раз­
ным шаблонам: один - для обычных, другой - для распродаваемых (рис. 7.1 9). В НТМL-странице из листинга 7.1 5 присутствуют два шаблона. Рис. 7. 19. Динамическая замена шаблона Листинг 7. 15. Динамическая замена шаблона
( dyna mi cTemplate\dynamicTemplate.js) <div id="tmpl Product" dat a-win-control ="WinJS.Binding.Templat e"> <div cl as s ="produc t i t em"> <h2 data-win-Ьind="innerText:name"></h2> Pri ce: <span data-win-bind="innerтext:pri ce"></span> </div> </div> <div id="tmpl ProductOnSal e" data-win-control ="WinJS.Binding.Templat e"> <div cl as s ="produc t i t em"> <h2 data-win-bind="innerText:name"></h2 > Price: <span data-win-bind="innerText:pri ce"></span> <Ь> ( On Sal e!) </Ь> </div> </div> <div i d="lvProduct s" data-win-control ="WinJS.UI.Li s tView"> </div> Динамическая замена шаблона ListView
Единственная разница между шаблонами для обычных и распро­
даваемых товаров заключается в том, что в последнем отображается еще сообщение «On Sale!�. Отметим, что шаблон элемента списка, используемый в элемен­
те управления L i s tVi ew, задается не декларативно, а императивно в JavaScгipt-кoдe, показанном в листинге 7.1 6. Ли сти нг 7.1 6. Динамическая замена шаблона ( dynamicTemplate\dynamicTemplate.js) ( funct ion ( )
11 use s tri ct"; funct i on ini t i al i z e ( )
WinJS.UI.processAl l ( ) .done ( funct i on ( )
1 1 Получаем ссылку на элемент управления Li s tView var lvProduct s = document.getElementByid ( "lvProduc t s" ) . '+winControl; 1 1 Создаем список товаров var l i s t Product s = new WinJS.Binding.Li s t ( [
{ name: "Bread", price: 2.2 0 }, { name: "Chees e •, price: 1.1 9, onSal e: true },
{ name: "Milk", price: 2. 3 3, onSal e: t rue } , { name: "Appl es •, price: 5.2 0 }, { name: "St eak", price: 1 2.9 9 } ] ) ; 1 1 Задаем функцию выбора шаблона элемента lvProduc t s.i t emTemplate = funct i on ( i t emPromi s e ) return it emPromi s e.then ( funct ion ( i t em) { 1 1 Выбираем шаблон для обычных или распродаваемых товаров var itemTemplate = document.getElementByid ("tmplProduct"); i f ( i t em.dat a.onSale ) { itemTemplate = document.getElementВyid ("tmplProductOnSale");
}; 1 1 Отрисовываем выбранный шаблон в контейнере DIV var container = document.creat eElement ("div"}; i t emTempl at e.winControl.render ( i t em.dat a, container );
return container; } } ; } ; 1 1 Привязываем список товаров к элементу Li s tView lvProduc t s.i t emDat aSource = l i s t Produc t s.dataSource; } ) ; document.addEvent Li s t ener ("DOMContentLoaded", ini t i al i z e );
} } ( }; Глава 7. Элемент управления ListView
Динамическая замена шаблона производится в следующем фраг­
менте, который требует некоторых пояснений. lvProduct s.i temTemplate = funct i on ( i temPromi se ) { };
return i temPromi se.then ( funct i on ( i tem) { 1 1 Выбираем шаблон для обычных или распродаваемых товаров var i t emTemplate = document.getEl ementByid ("tmpl Product"); i f ( i t em.data.onSale ) { i temTemplate = document.getElementByid ("tmpl ProductOnSal e"); } ; 1 1 Отрисовываем выбранный шаблон в контейнере DIV var container = document.creat eElement ("div"); i temTemplate.winControl.render ( i t em.dat a, container );
return container; } ) ; Всякий раз как отрисовывается очередной элемент списка Li s tView, функции передается обещание. Объект данных, ассо­
циированный с элементом списка недоступен, пока обещание не будет исполнено. Отметим, что у параметра i tem, передаваемого функции, имеется свойство data, которое представляет объект, ас­
социированный с этим элементом списка Li s tView. Чтобы узнать, распродается ли данный товар, достаточно опросить свойство i tem. data. onSale. Когда обещание исполняется, мы можем вернуть фрагмент НТМL­
кода, отрисовываемый для данного элемента списка ListView. В при­
мере выше этот фрагмент формируется с помощью одного из двух шаб­
лонов: tmpl Product (для обычных товаров) или tmpl ProductOnSale
(для распродаваемых товаров). Разметка выбранного шаблона погру­
жается в контейнерный DIV, который и возвращает функция. Посте пенная з агруз ка э лемент ов спис ка List Vi ew Пусть требуется использовать элемент управления Li s tView для просмотра гигантского каталога товаров, содержащего тысячи на­
именований. Как организовать эффективную прокрутку такого ка­
талога? Элемент Li s tView автоматически поддерживает подгрузку дан­
ных по мере необходимости. Если привязать Li s tView к источнику данных, сол;ержаще11-1у тысячи объектов, то будет загружено и отри-
Постепенная загрузка элементов списка ListView
совано лишь столько объектов, сколько нужно для заполнения теку­
щей, предыдущей и последующей страницы. Это не тысячи объектов, а гораздо меньше. Этот механизм прекрасно работает, когда данные можно загрузить быстро, но не годится, если данные загружаются по сети (с помощью Аj ах-запроса). В таком случае, чтобы не раздражать пользователя, не­
обходимо загружать данные в элемент Li s tView постепенно. Для управления процессом загрузки данных из источника в эле-
мент Li s tView предназначены следующие свойства. automaticallyLoadPages - получить или установить булевс­
кое свойство, которое определяет, будет ли Li s tView автомати­
чески подгружать дополнительные страницы. Автоматическая подгрузка происходит, когда пользователь прокручивает боль­
ше страниц, чем задано в свойстве pagesToLoadThreshold. loadingBehavior - получить или установить стратегию подгрузки дополнительных данных. Возможные значения -
"randomacces s" и "incremental".
loadingState - получить строку, описывающую состояние загрузки. Возможные значения -"complete" (завершена) и "loading" (загружается). pagesToLoad - получить или установить целое число, опреде­
ляющее, сколько страниц загружать. Это свойство использует­
ся, если задан режим automaticallyLoadPages или вызывает­
ся метод loadMorePages ( ). pagesToLoadThreshold - получить или установить целое число, определяющее, когда загружать новый набор страниц. Если установлен режим automaticallyLoadPages и пользо­
ватель прокрутил больше pagesToLoadThreshold страниц, то автоматически подгружаются новые страницы. Кроме того, с помощью описанного ниже метода можно принуди-
тельно заставить Li s tView загрузить дополнительные данные: loadМorePages - если свойство loadingBehavior равно incremental, то этот метод заставляет элемент Li s tView заг­
рузить дополнительные страницы. Количество страниц опре­
деляется свойством pages ToLoad.
Наконец, элемент Li s tView генерирует следующее событие, когда занимается подгрузкой данных: loadingsta techanged- генерируется, когда элемент Li s tView
заканчивает загрузку. Глава 7. Элемент управления ListView
Свойство loadingBehavior элемента Lis tView определяет, будет ли элемент подгружать данные постепенно. Если этот режим включен, то с помощью свойства automaticallyLoadPages можно уточнить, производить ли подгрузку дополнительных страниц автоматически или вручную. Например, код в листинге 7.1 7 создает массив, содержащий 10 ООО товаров (немаленький каталог!). Ли ст инг 7. 1 7. Постепенная загрузка данных (incremental\i ncremental .js) ( funct i on ( ) { 11 use stri ct"; funct i on ini t i al i z e ( )
WinJS.UI.processAl l ( ) .done ( func t i on ( ) { 1 1 Получаем ссылку на элемент управления Li s tView var lvProduct s = document.getElementByi d ("lvProduct s"). '+winControl; 1 1 Создаем массив товаров var product s = [ ]; for ( var i = О; i < 1 0 0 0 0; i + + ) { product s .push ( { name: /1 Product 11 + i / price: Math.f loor ( ( Math.random ( ) * 5 0 0 0 ) + 1 ) } ) ; 1 1 Создаем список товаров var l i s t Product s = new WinJS.Binding.Li s t ( product s );
1 1 Разрешаем постепенную загрузку lvProduct s.loadingBehavior = "incremental" lvProduct s.automat ical lyLoadPages = true ; lvProduc t s.pagesToLoad = 1 0; 1 1 Привязываем список товаров к элементу Li s tView lvProduct s.i temDataSource = l i s t Product s.dataSource; } ) ; document.addEventLi s tener ("DOMCont entLoaded", ini t i al i z e );
} ) ( ); Здесь постепенная загрузка настраивается в следующих строч­
ках: lvProduct s.loadingBehavior = "incremental" lvProduct s.automat ical lyLoadPages = true ; lvProduct s.pagesToLoad = 1 0; Резюме •••••ШJ При первой отрисовке списка Li s tView в нем отображаются толь­
ко первые 1 52 элемента. Когда в результате прокрутки достигается конец загруженной порции, считываются и загружаются следующие 1 52 элемента. Элементы добавляются в список постепенно. Предупреждение. Точное количество элементов, загруженных в Li s tView,
зависит от того, сколько элементов помещается на одной странице. От­
метим, что свойство pages ToLoad определяет количество подгружаемых страниц, а не элементов. Число элементов зависит как от ширины и высоты Lis tView, так и от ширины и высоты отдельного элемента списка. Lis tView сам вычисляет, сколько элементов он может одновременно пока­
зать на экране ( размер страницы). Если, например, на странице помещается 1 0 элементов, а свойство pagesToLoad равно 5, то будет подгружено 70 эле­
ментов (дополнительные 20 - для страниц до и после загруженной порции). Р ез юме Эта глава была целиком посвящена элементу управления Li s tView.
Вы узнали, как с его помощью можно отображать коллекции данных в виде списка или сетки. Я начал с рассмотрения простых возможностей этого элемента. Затем мы обсудили выбор, сортировку, фильтрацию и группировку списка Lis tView. Также были рассмотрены некоторые дополнитель­
ные возможности, в том числе динамическая замена шаблона и пос­
тепенная загрузка данных. В следующей главе я покажу, как использовать нестандартные источники данных для элемента Lis tView, в том числе базу данных IndexedDB и веб-службу. - - -
rЯАВА 8. Соз да ние ист о ч ник о в да нны х В этой zлаве: • Создание нестандартного источника данных.
• Файл как источник данных.
• Веб-служба как источник данных.
• База данных IndexedDB как источник данных.
В состав библиотеки WinJS включены два объекта, которые могут служить источниками данных: Lis t и StorageDataSource. Если тре­
буется, чтобы элементы Li s tView или FlipView получали данные из других источников, то написать их придется самостоятельно. Цель итой главы - объяснить, как пишутся источники данных. Я объясню это на примере трех нестандартных источников: FileDataSource -позволяет получать данные из локального
файла; WeЬServiceDataSource - позволяет получать данные от
удаленной веб-службы (и там же сохранять их); IndexedDBDataSource - позволяет получать и сохранять
данные в базе данных IndexedDB. Полный исходный код всех трех источников данных можно найти в проекте на сайте GitHub по адресу https://github.com/Stephe11 Wal ­
theг/Windows8Apps U 11leashed. Созд ание неста ндартного исто чника данных Я начну с общих сведений о том, как создается нестандартный ис­
точню< данных. В этом разделе я объясню, как реализовать методы Создание нестандартного источника данных источника данных, в котором данные хранятся в массиве J avaScгipt. Я расскажу о конструкторе и наиболее важных методах такого источ­
ника. Обсудив этот - до предела упрощенный - источник данных, мы далее перейдем к практически полезным источникам, в том числе веб-службам и базе данных IndexedDB. Создание класса источника данных Простейший способ создать источник данных - унаследовать ба­
зовому классу Virtual i z edDataSource. Например, источник MyDa taSource можно создать следующим образом: var MyDat aSource = WinJS.Cl ass.derive ( WinJS.UI.Virtual i zedDataSource, ) ; 1 1 Конструктор funct i on ( ) { thi s._adapter = new MyDat aAdapter ( ); thi s._bas eDataSourceConstructor ( thi s._adapter ); Здесь методу WinJS. Clas s. deri ve ( ) передано два аргумента: базовый класс (winJS. ur. Virtual i zedDataSource) и конструктор нового класса. В конструкторе производного класса Virtualiz edDataSource мы вызываем метод _baseDataSourceContructor ( ),передавая ему адаптер данных. Основная часть работы как раз и заключается в со­
здании этого класса адаптера. Создание адаптера данных Для создания адаптера данных необходимо реализовать интерфейс IListDataAdapter, в котором объявлены следующие методы: change ( ) getCount ( ) insertAfter ( ) insertAtEnd ( ) insertAtStart ( ) insertBefore ( ) items FromDescription ( ) i tems FromEnd ( ) items Fromi ndex ( ) i tems FromKey ( ) items FromStart ( ) itemSignature ( ) moveAfter ( ) moveBefore ( ) moveToEnd ( ) moveToStart ( ) remove ( ) setNotificationHandler ( ) Глава 8. Создание источников данных В интерфейсе также объявлено одно свойство: compareByi dentity К счастью, реализовывать все эти методы необязательно, доста­
точно ограничиться теми, что необходимы в конкретном случае. Например, если требуется создать простой источник данных, допускающий только чтение, то нужно реализовать лишь методы getCount ( ) и i temsFromindex ( ). Для более сложного источника, допускающего еще и запись, придется реализовать также методы change ( ), insertAtEnd ( ) и remove ( ). В следующих разделах я опишу, как реализовать методы getCount ( ) И i tems Fromi ndex ( ). Реализация метода getCount() Метод getCount ( ) должен возвращать общее число записей в источ­
нике данных. Например, если данные хранятся в массиве JavaScгipt, то метод getCount ( ) должен вернуть длину массива: getCount: funct i on ( ) { return WinJS.Promi s e.wrap ( thi s._arrayDat a.l ength ); Метод getCount ( ) должен возвращать объект Promi se. Поэтому требуется либо обернуть значение объектом Promi s e с помощью ме­
тода WinJS. Promi se. wrap ( ), либо создать объект Promi s e самосто­
ятельно. Реализация метода itemsFromlndex() Метод i tems Fromindex ( ) возвращает набор объектов из источника данных. Ему передаются три аргумента: requestindex - индекс опорного объекта, извлекаемого из ис­
точника; Создание нестандартного источника данных 1111 ••tи1 countBefore - количество извлекаемых объектов с индексами меньше опорного; countAfter - количество извлекаемых объектов с индексами больше опорного. Параметры countBefore и countAfter интерпретируются как по­
желания. Реализация может вернуть больше объектов, чем запроше­
но В countBefore или countAfter. Ниже показана простая реализация метода i tems Fromindex ( ), в которой возвращается часть мaccивaJavaScгipt. i temsFromindex: funct i on ( request index, countBefore, countAfter ) var s t art index = Math.max ( O, request index - countBefore ); var suЫt ems = thi s._arrayDat a.s l i ce ( s t art index); return WinJS.Promi s e.wrap ( { i t ems: suЫt ems, o f f s et: reques t index - s t art index, totalCount: this._arrayDat a.l ength } ) ; Метод i tems Fromindex ( ) должен вернуть объект, реализую­
щий интерфейс I FetchResult. В примере выше объект обладает тремя свойствами, объявленными в I FetchRe sult: items, offset и totalCount. Свойство i tems представляет массив объектов, возвращенных ме­
тодов i tems Fromindex ( ).Каждый объект должен реализовывать ин­
терфейс I Lis t rtem. Как минимум, у объекта должны быть свойства key и data. Свойство offset - это позиция объекта, соответствующего запро­
шенному опорному индексу reques ti ndex, в массиве i tems. Если возвращены также элементы с индексами, меньшими опорного, то значение offset будет больше нуля. Наконец, в свойстве totalCount возвращается общее число объ­
ектов в источнике данных, а не только число возвращенных. Каждый объект в массиве i tems, возвращенном методом i tems Fromindex,дoлжeн реализовывать интерфейс I Lis ti tem. Ниже приведен пример данных, полученных от метода i tems Fromindex ( ): i tems: [ { key:"l", dat a: { name: "проснуться"} }, { kеу:"З", dat a: { name: "встать с кровати"} }, { key:"4", dat a: { name: "причесаться"} } of f s et: О, ШI••••� Глава 8. Создание источников данных totalCount: 3 0 Примечание. Помимо метода i tems Fromindex ( ), можно также реализо­
вать методы i tems FromDescription ( ), i tems FromEnd ( ), i temsFromKey ( ) и i tems FromStart ( ). Но для выборки данных эти методы необязательны. Реализация метода insertAtEnd() Чтобы можно было создавать новые объекты, необходимо реализо­
вать метод insertAtEnd ( ). Ему передаются два аргумента: ключ но­
вого объекта и собственно объект. В примере ниже показана реализация insertAtEnd ( ),в которой новый объект добавляется в конец мaccивajavaScгipt. insertAtEnd: funct ion ( key, dat a) { var newitem = { key: ( ++thi s._maxKey) .t oString ( ), data: data } ; thi s._arrayDa t a.push ( newi tem); return WinJS.Promi s e.wrap ( newi tem); Обычно вы не передаете ключ, а оставляете его генерирование на усмотрение источника данных. Иными словами, аргумент key, как правило, игнорируется. В приведенном примере ключ генерируется путем увеличения последнего ранее сгенерированного ключа на еди­
ницу и преобразования результата в строку. Предупреждение. Ключи объектов в источнике данных должны быть стро­
ковыми значениями. Отметим, что метод insertAtEnd ( ) возвращает новый объект, включающий ключ. Метод обязан вернуть объект, который реализует интерфейс I I tem, обернутый обещанием. Реализация метода remove() Чтобы иметь возможность удалять объект из источника данных, не­
обходимо реализовать метод remove ( ) , которому передается ключ удаляемого объекта. Если объект успешно удален, то метод не воз­
вращает ничего. Ниже приведен код, в котором объект удаляется из массива. remove: funct i on ( key ) { var i = this._get indexFromKey ( key ); Создание нестандартного источника данных this._arrayData.spl i c e ( i, 1 ); return WinJS.Promi s e.wrap ( nul l }; } ' getindexFromKey: funct i on ( key} { for ( var i = О; i < thi s._arrayData.l engt h; i + + ) { i f ( thi s._arrayData ( i J .key == key) { return i · Реализация метода change() Чтобы можно было редактировать данные в источнике, необходимо реализовать метод change ( ) , которому передаются три аргумента: ключ изменяемого объекта, его новое значение и индекс изменяемого объекта. Ниже приведен пример реализации метода change ( ) : change: funct i on ( key, dat a, indexHint ) { var newit em = { key: key, dat a: dat a } ; var i = this._get indexFromKey ( key ); thi s._arrayData ( i ] = dat a; return new WinJS.Promi s e.wrap ( nul l ); } ' _get indexFromKey: funct i on ( key) ( for ( var i = О; i < this._arrayDat a.l engt h; i ++ ) { i f ( thi s. _arrayDat a [ i J . key = = key) ( return i; Отметим, что метод change ( ) возвращает пустое обещание. В мо­
мент его исполнения метод просто завершается, ничего не возвра­
щая. Обработка ошибок Если при вставке, удалении или изменении возникнет ошибка, то можно вернуть объект Edi tError с одним из следующих четырех значений: tm•••• Глава 8. Создание источников данных WinJS.UI.EditError.canceled - операция редактирования по какой-то причине отменена; WinJS.UI.EditError.noRe spons e - операция редактирова­
ния прекращена из-за таймаута; WinJS.UI.EditError.notPermitted - попытка писать в ис­
точник данных, допускающий только чтение; WinJS. UI. Edi tError. noLongerMeaningful - объект уже был изменен. Например, в источнике данных, допускающем только чтение, ме­
тод insertAtEnd ( ) можно реализовать следующим образом: insertAtEnd: funct i on ( unused, data ) { return WinJS.Promi s e.wrapError ( пеw WinJS.ErrorFromName ( WinJS.UI.Edi tError.not Permi tted) ); Реализация метода setNotificationHandler() Метод setNotificationHandler ( ) позволяет генерировать уведом­
ления при изменении состояния источника данных. Определены сле­
дующие методы: beginNotifi cations ( ) changed ( ) countChanged ( ) endNotifications ( ) indexChanged ( ) inserted ( ) invalidateAl l ( ) itemAvailaЫe ( ) moved ( ) reload ( ) removed ( ) Генерировать уведомления необязательно при использовании стандартных методов адаптера данных, таких как insertAtEnd ( ), remove ( ) и change ( ) . Но это необходимо, когда вы добавляете в ис­
точник какой-нибудь нестандартный метод. Пусть, например, в источник данных добавляется метод nuke ( ), при вызове которого из источника удаляются все вообще данные. Вот как можно было бы реализовать его в классе адаптера данных: setNot i f icationHandler: funct ion ( not i f icat i onHandler ) this._not i f icat i onHandler = not i f i cat ionнandler; Файл как источник данных } ' nuke: func t i on ( ) { this._arrayDat a = [ ]; this._not i f i cat i onHandl er.reload ( ); Здесь метод s etNotificationHandler ( ) записывает предопре­
деленный метод notificationHandler в закрытую переменную _ notifi ca t ionHandler. В результате этот предопределенный метод notificationHandler становится доступен всем методам адаптера данных. Нестандартныйметодnukе ( ) присваиваетмассиву-источникупус­
той массив и вызывает метод reload ( ) oбъeктa notificationHandler. Если не сделать этого, то элемент Li s tView не очистит свое содержи­
мое. Ниже показано, как источник данных раскрывает нестандартный метод nuke ( ) : var MyDataSource = WinJS.Cl as s.derive ( WinJS.UI.Virtual i z edDataSource, 1; 1 1 Конструктор funct i on ( f i l eName ) { this._adapter = new MyDataAdapter ( ); this._baseDataSourceConstruct or ( thi s._adapter ); } ' 1 1 Методы экз емпляра { nuke: funct ion ( ) { thi s._adapt er.nuke ( ); Показанный выше класс MyDataSource содержит метод nuke ( ), который просто делегирует вызов методу nuke ( ) адаптера данных. Ф айл как источник данных В этом разделе я покажу, как можно реализовать файловый источник данных. Этот источник получает и сохраняет данные в файловой сис­
теме. Примечание. Полный исходный код класса FileDataSource находитсs� в папке fi l e проекта на сайте Gi tHub. ШJ• • ••� Глава 8. Создание источников данных В классе FileDataSource реализованы методы getCount ( ), i tems Fromindex ( ), insertAtEnd ( ), remove ( ) и change ( ). При вызове методов getCount ( ) и i tems Fromindex ( ) объект Fi leDataSource загружает данные из файловой системы и вызывает метод JSON .parse ( ),чтобы преобразовать ихв массивJаvаSсгiрt. Заг­
рузка данных производится в методе _ensureData ( ): _ensureData: funct ion ( ) { var that = thi s; 1 1 Пытаемся вернуть кэшированные данные i f ( this._cachedDat a ) { return WinJS.Promi s e.wrap ( that._cachedDat a); 1 1 Иначе загружаем данные return new WinJS.Promi s e ( funct i on ( complet e, error ) { var local = WinJS.Appl icat ion.l ocal; var def = '{"maxKey":- 1,"i tems":[ ] }'; local.readText ( that._f i l eName, de f ) .done ( funct i on '+ ( f i l eCont ent s ) { that._cachedData = JSON.pars e ( f i l eContent s ); compl ete ( that._cachedDat a); } ) ; } ) ; Метод readText ( ) читает текстовый файл из файловой системы. Если такого файла не существует, то readText ( ) возвращает значе­
ние def. В любом случае результат чтения преобразуется в массив JavaScгipt с помощью метода JSON. parse ( ). При вызове любого из методов insertAtEnd ( ), remove ( ) или change ( ) объект FileDataSource обращается к методу JSON. stringi fy ( ) для преобразования данных в строку, а затем сохраняет результат в файловой системе. Для сохранения данных предназначен метод s aveDa ta ( ) : _s aveData: funct i on ( data ) { this._cachedData = dat a; var local = WinJS.Appl icat i on.local; var str = JSON.stringi fy ( data ); return local.writeText ( thi s._f i l eName, s tr ); Метод _ saveDa ta ( ) преобразует массив JavaScгipt в строку, поль­
зуясь методом JSON. s tringify ( ). Полученная строка занисывается в файловую систему методом wr i teтext ( ). Файл как источник данных Использование файлового источника данных Пусть требуется создать простое приложение для ведения списка за­
дач, причем источником данных является файл. Приложение должно уметь показывать список существующих задач и создавать новые за­
дачи (рис. 8.1 ). Мы хотим еще, чтобы задачи можно было удалять по отдельности -
сначала отметив задачу щелчком правой кнопкой мыши или жестом смахивания, а затем нажав кнопку Delete (рис. 8.2) - или все сразу -
нажав кнопку Nuke. Рис. 8.1. Список задач, полученный из файлового источника данных Рис. 8.2. Удаление задач из файлового источника данных Пользовательский интерфейс приложения приведен в листин­
ге 8.1. На этой НТМL-странице присутствует элемент управления Lis tView, содержащий список задач. Листинг 8. 1. Фай ловый исто чник да нных (file\fi le.html) <!DOCTYPE html> <html> <head> <meta charset ="ut f - 8" /> Глава 8. Создание источников данных <t i t l e>Li s t Data Source</t i t le> <!- - Ссылки на WinJS �-> < l ink hre f ="//Microsof t.WinJS.1.0/c s s/ui -dark.cs s • '+re l ="s tyl esheet • /> <script src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <script src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на источники данных - - > < l ink hre f = • f i l e.cs s • rel ="s tyl esheet • /> <script type= • t ext/j avascript • src ="f i l eDataSource.j s"> '+</s cript> <script src="f i l e.j s"></script> </head> <body> <div id="tmplTask" data-win-contro l ="WinJS.Binding.Template"> <div c l as s = • taskitem"> <span data-win-Ьind="innerText:name"></span> </div> </div> <div id="lvTasks" data-win-control ="WinJS.UI.Li s tView• data-win-opt i ons ="{ i temTemplate: select ('#tmplTask'), sel ecti onMode: 's ingl e' } "> </div> < form id="frmAdd"> < f ieldset> <legend>Add Task</legend> <label>New Task</l abel > <input id="inputTaskName • required /> <butt on>Add</button> </f ieldset > </form> <button i d="btnNuke">Nuke</but ton> <button id="btnDel ete">Del ete</but t on> <button id="btnEdi t">Edit </button> </body> </html > JavaScгipt-фaйл, ассоциированный с этой страницей, приведен в листинге 8.2. В нем элемент управления Lis tView привязывается к источнику данных F i l eD at aS o ur ce. Лис т инг 8.2. Файловый источник да нных (file\file.js) /// <reference path="//Micros of t.WinJS.1.0/j s/base.j s • /> 1 1 1 <ref erence path="//Microsof t.WinJS.1.0/j s/ui.j s • /> funct i on init ( ) { Файл как источник данных WinJS.UI.proces sAl l ( ) .done ( funct i on ( ) { var lvTasks = document.getElementByid ("lvTasks") .winControl; 1 1 Создаем источник данных и привязываем его к Li s tVi ew var tasksDataSource = new DataSources.FileDataSource ("tasks.j son"); lvTasks.i temDataSource = tasksDataSource; 1 1 Настраиваем обработчики формы frmAdd и кнопок Delete и Nuke document. getElementByid ( "frmAdd") . addEventLi s t ener ( "submi t", funct i on ( evt ) { evt.preventDefaul t ( ); tasksDat aSource.beginEdi t s ( ); t asksDataSource.insertAtEnd ( nul l, name: document.getElementByid ("inputTaskName") .value } ) . done ( funct i on ( newi tem) { tasksDataSource.endEdi t s ( ); document.getElementByi d ("frmAdd") .reset ( ); lvTasks.ensureVi s iЬle ( newit em.index ); } ) ; } ) ; document.getElementByid ("btnDel et e") .addEventLi s t ener ("c l ick", funct i on 1 ) { i f ( lvTasks.sel ecti on.count ( ) = = 1 ) lvTasks.sel ecti on.get i t ems ( ) .done ( funct i on ( i tems ) tasksDataSource.beginEdi t s ( ); tasksDataSource.remove ( i tems [ O ] .key) .done ( funct i on ( ) tasksDataSource.endEdi t s ( ); } ) ; } ) ; } } ) ; document.getElementByid ("btnEdit") .addEventLi s tener ("c l ick", '+func t i on ( ) { i f ( lvTasks.sel ecti on.count ( ) == 1 1 lvтasks.sel ecti on.get i t ems ( ) .done ( func t i on ( i t ems ) tasksDat aSource.beginEdi t s ( ); tasksDataSource.change ( i t ems [ O ] .key, { name: "Changed!" } ) . '+done 1 funct i on 1 1 { tasksDataSource.endEdi t s ( ); } ) ; } } ) ; } 1; document. getEl ementByid ( "btnNuke") . addEventLi s t ener ( "c l ick", '+ funct i on ( ) { t asksDataSource.nuke ( ); } ) ; EШ• • l l l Глава 8. Создание источников данных } ) ; document.addEventLi stener ("DOMCont entLoaded", ini t ); Файловый источник данных создается и привязывается к Li s tView в следующих строчках: var tasksDataSource = new DataSources.Fi leDataSource ("tasks.j son"}; lvTasks.i t emDataSource = tasksDataSource; Имя создаваемого файла передается конструктору класса FileDataSource. В нашем случае список задач хранится в файле tasks. j s on. Для создания новых задач служит следующий код: document.getElementByid ("f rmAdd"} .addEventLi s t ener ("submi t", functi on ( evt } { evt.preventDefaul t ( }; tasksDataSource.beginEdi t s ( }; tasksDataSource.insertAtEnd ( nul l, name: document.getElementByid ("inputTaskName"} .value } ) .done ( funct i on ( newi t em} { tasksDataSource.endEdi t s ( }; document.getElementByid ("frmAdd"} .reset ( }; lvTasks.ensureVi s iЬle ( newit em.index}; } } ; } } ; При отправке формы для добавления новой задачи вызывается метод insertAtEnd ( ) источника данных. Если задача успешно со­
здана, то вызывается метод ensureVi s iЫe ( ) элемента управления Li s tView, чтобы новая задача оказалась видна в списке. Предупреждение. Не заб ыва йте вызы вать ме тоды begi nEdi ts ( } и end Edi ts ( } при вст авк е, редак ти ров ании или уда лении да нных из источ ни­
ка. В проти вном случае при сл ишком бы стром выпо лнении опер аци й редак­
тирования можно получ ить исключение из-за нулевой ссыл ки. В еб-сл уж ба как источник данных В этом разделе я расскажу, как построить источник, получающий данные от веб-службы. Это позволит нам привязать элемент управле­
ния Li s tView к удаленной веб-службе. Источник позволит получать и модифицировать данные. Веб-службу как исто<1ник данных можно использовать, например, для получения списка товаров из каталога, хранящегося в базе данных Веб-служба как источник данных на удаленном веб-сайте. В этом случае при любом обновлении каталога ваше приложение Магазина Windows будет показывать актуальное состояние. Примечание. При работе с Ajax я настоятельно рекомендую использовать бесплатную программу Fi ddl er2 для отладки Аjах-заnросов. Скачать Fi ddl er2 ( которая прекрасно работает с Wi ndows 8) можно с сайта http://fi ddl er2.com. Примечание. Полный исходный код класса WebServi ceDataSource находится в nanкe webService проекта на сайте Git Hub. Создание источника данных При использовании удаленной веб-службы в качестве источника данных применяется метод WinJS. xhr ( ) для отправки Аjах-вызовов. Например, метод getCount ( ) выглядит следующим образом: getCount: funct i on ( ) { var that = this ; return new WinJS.Promi s e ( func t i on ( complete, error ) { var opt i ons = { } ) ; url: that._url + "/getCount" }; return WinJS.xhr ( opt i ons ) .then ( funct i on ( xhr ) { var count = JSON.parse ( xhr.respons e ); complete ( count ); } ' funct i on ( xhr ) { cons ol e.log ("Coul d not cal l getCount ( )"); } ) ; Метод getCount ( ) обращается к действию getCount ( ) удаленной веб-службы, отправляя запрос НТТР GЕТ. Показанный ниже метод remove ( ) обращается к одноименному действию веб-службы, отправляя запрос НТТР D ELETE: remove: funct ion ( key ) { var that = this ; return new WinJS.Promi s e ( funct ion ( compl ete, error ) { var opt i ons = { url: that._url + "/remove/" + key, type: "DELETE" , headers: { } } ; authent i cat ionToken: that. aut hent icat i onToken WinJS.xhr ( opti ons ) .then ( funct i on ( xhr ) { compl ete ( ) ; } ' funct i on ( xhr ) Глава 8. Создание источников данных consol e.log ( "Could not cal l remove ( )" ); ) ; } ) ; } Вообще, любой метод источника данных делегирует свою работу одноименному действию удаленной веб-службы. Создание веб-службы Создать веб-службу можно разными способами. Даже если ограни­
читься только технологиями Майкрософт, все равно есть несколько вариантов. Это может быть WСF-служба, служба данных WCF, веб­
служба ASMX, службы Windows Аzше MoЬile Seгvices или служба на базе ASP.NET Web API. Я решил создать веб-службу на базе ASP.NET Web API. Добавить контроллер Web API можно как в проект ASP.NET Web Foгms, так и в проект ASP.NET MVC. Я остановился на проекте типа ASP.NET MVC 4. При создании проекта я выбрал шаблон «Проект Web APli> (рис. 8.3). �J b.J bJ �J f,.,pty l.!�s« 0,-..:tiчt �р,Чv.�,.. �;�:,<W�� Рис. 8.3. Создание проекта ASP.NET MVC 4 по шаблону Web API Веб-служба как источник данных Для создания веб-службы ASP.NET Web API необходимо доба­
вить нужные маршруты в файл Арр _ Staгt\RouteConfig.cs. Маршру­
ты сопоставляют запросам, поступающим от браузера, правильный контроллер и действие. Я добавил в файл App_Staгt\RouteConfig.cs следующие маршру-
ты: routes.MapHttpRoute ( name: "TasksGetCount", routeTemplate: "api/tasks/getCount", defaul t s: new { control l er= "tasks" , act i on= "getCount" } ) ; routes.MapHt tpRout e ( name: "Tasks i t emsFromindex", rout eTemplate: "api/t asks/i t emsFromindex", default s: new { control l er = "tasks" , action ) ; routes.MapHttpRoute ( name: "Tasks insertAtEnd", routeTemplate: "api/tasks/insertAtEnd", def aul t s: new { control ler = "tasks" , act i on 1; routes.MapHt tpRoute ( narne: "TasksRernove н, rout eTemplate: "api/tasks/remove/{ key}", de faul t s: new { cont rol l er = "t asks" , act i on ) ; routes.MapHt tpRoute ( name: "TasksNuke", rout eтemplate: "api/tasks/nuke", "itemsFromindex" } "insertAtEnd" } "remove" } defaul t s: new { control l er = "t asks" , act i on = "nuke" } ) ; Например, если отправить НТТР GЕТ-запрос по адресу /api/ tasks/getcount, то будет вызвано действие Getcount ( ) контроллера Web API Tas ks Controller. У контроллера Tas ks Controller име­
ются действия GetCount { ), I tems Fromi ndex ( ), I nsertAtEnd ( ), Remove ( ) и Nuke ( ).Для взаимодействия с таблицей задач в базе дан­
ных действия пользуются технологией Micгosoft Entity Fгamewoгk. Во как выглядит действие GetCount ( ): [ HttpGet ] puЬl ic int GetCount ( 1 { return _db.Tasks.Count ( ); Метод GetCount ( ) возвращает общее число задач в таблице Tasks базы данных. Обратите внимание, что метод GetCount ( ) снабжен Глава 8. Создание источников данных атрибутом [ н t t pGe t J , который означает, что это действие вызывается только в ответ на Аjах-запрос типа GЕТ. Действие Remove ( ) контроллера выглядит так: [ Ht tpDel ete] puЫi c bool Remove ( s tring key) { var id = int .Parse ( key); _db.Tasks.Remove ( _db.Tasks.Find ( id ) ); _db.SaveChanges ( ); return true; Действие Remove ( ) принимает аргумент key - ключ удаляемого объекта. Ключ должен быть строкой, потому что таково требование к источнику данных для элемента управления Li s tView. Отметим, что метод Remove ( ) снабжен атрибутом [ Ht tpDelete J, означающим, что это действие может быть вызвано только в ответ на запрос типа НТТР DELETE. При работе с НТТР-методами DELETE и PUT есть одна тонкость. В корневой файл Web.config приложения ASP.NET MVC необходимо добавить такую секцию: < system.webServer> <modules runAl lManagedModul esForAl lRequest s ="true"> </modules> </system.webServer> Если этого не сделать, то на все запросы типа PUT и DELETE бу­
дет возвращаться ответ с кодом состояния 404. Использование веб-службы как источника данных После того как удаленная веб-служба написана и сконфигурирована, воспользоваться ей совсем просто. Ниже показано, как создать эк­
земпляр источника данных и привязать его к элементу управления Lis tView с идентификатором lvTas ks: var lvTasks = document.getElementByid ("lvTasks") .winControl; var tasksDataSource = new DataSources.WebServiceDataSource ("http://localhost:5 1 8 0 7/api/ 4tasks", "id"); lvTasks.i temDataSource = tasksDataSource; Конструктору экземпляра источника данных необходимо передать два аргумента: URL-aдpec веб-службы и имя первичного ключа извлекаемых данных. База данных l ndexed DB как источник данных В данном случае URI� http:j /localhost:51 807 /api/tasks адресует локальную веб-службу на порту 5 1 807. Номер порта генерируется случайно. Узнать его можно, открыв в Visual Studio окно свойств проекта и перейдя на вкладку Web (рис 8.4). (�) Use Local 115 VVeb 5-eru:r ;_;;:и�:: >1:;�:::;�, Project Url: О Ove:rrlde appl icatian rcюt URL l1hp:/llc<al'1oct:51S07/ Рис. 8.4. Определение номера порта, назначенного проекту Visual Studi o Примечание. Если вы не хотите создавать собственные службы с помо­
щью технологии ASP. NET Web API, то можно воспользоваться замечательной службой Wi ndows Azure Mobi l e Servi ces, которая позволяет без особого труда сохранять данные в облаке Wi ndows Azure и выбирать их оттуда. Дополни­
тельные сведения по этому поводу см. по адресу https://www.wi ndowsazure. com/en- us/devel op/moЬi l e/. Б аз а данных l ndexed DB как источ ник данных Indexed Database API ( I ndexedDB) - это рекомендация WЗС на тему интерфейса к базам данных из браузера. Эту рекомендацию подде­
рживают браузеры Fil'efox 1 2, Chiome 1 9 и IE 1 0. И разумеется - при­
ложения Магазина Windows. В разных браузерах для реализации IndexedDB API применяются различные базы данных. Например, в Fiгefox используется SQLite, а в IE - SQLCE. IndexedDB API предоставляет стандартный способ взаимодействия с базами данных, не зависящий от браузера и платформы. Если в приложении Магазина Windows требуется хранить большие объемы данных и предъявлять к ним запросы, то IndexedDB может оказаться неплохим выбором. Располагая поддержкой индексов и курсоров, IndexedDB способна эффективно работать с большими наборами данных. В этом разделе мы рассмотрим, как создать источник данных на основе IndexedDB. Я продемонстрирую это на примере простого при-
Глава 8. Создание источников донных ложения, позволяющего фильтровать список фильмов по категории (рис. 8.5). Рис. 8.5. Использование l ndexedDB в качестве источника данных Общие сведения о lndexedDB База данных IndexedDB отличается от баз данных, к которым вы, ве­
роятно, привыкли. Это объектно-ориентированная, а не реляционная база. Данные хранятся не в таблицах, а в хранилищах объектов. База данных IndexedDB может содержать одно или несколько хранилищ, в каждом из которых хранится коллекцияJаvаSсгiрt-объектов. В IndexedDB API предусмотрены как синхронные, так и асинх­
ронные методы. В настоящее время, браузеры поддерживают только асинхронные методы. Операции подключения к базе данных, добав­
ления объекта в хранилище или получения количества объектов в хранилище выполняются асинхронно. Создание баз ы данных l ndexed DB и подкл юче ние к ней База данных I ndexedDB заранее не создается; это происходит при первом обращении к ней. Для создания новой базы следует обрабо­
тать событие upgradeneeded, генерируемое при подключении к базе. Ниже показано, как подключиться к существующей базе данных TasksDB и создать новую базу, если она еще не существует: var reqOpen = window. indexedDБ. open ( "TasksDБ", 2 ) ; reqOpen.onupgradeneeded = func t i on ( evt ) { База данных l ndexDB как источник данных l l l • •EИ var newDB = evt.target.resul t; newDB. creat eObj ect St o re ( "t ask s", { keyPa th: "i d" , au t oi ncre rne nt: true } ) ; } ; reqOpen. onsuccess = func t i on ( ) { var db = reqOpen.resul t; 1 1 Сделать что-то с db } ; Если при вызове метода window. indexedDB. open ( ) база данных еще не существует, то генерируется событие upgradeneeded. В при­
мере выше обработчик этого события создает новое хранилище объ­
ектов с именем tasks. В этом хранилище есть автоинкрементный столбец id, играющий роль первичного ключа. Если уже существует нужная версия базы данных, то при вызове метода window. indexedDB. open ( ) генерируется событие succes s. В этот момент у вас есть открытое подключение к базе данных, так что можно делать что-то полезное. Доба вл ение объек тов в хранилище Для взаимодействия с базой данных IndexedDB применяются асинхронные методы. Так, в примере ниже показано, как добавить новый объект в хранилище объектов tasks: var t ransact i on = db.trans act i on ("t asks", "readwri t e"); var s t ore = trans ac t i on.obj ectStore ("t asks"); var reqAdd = s t ore.add ( { name: "Покормить собаку" } ) ; reqAdd.onsuccess = func t i on ( ) 1 1 Задача успешно добавлена } ; Здесь создается транзакция чтения-записи, в хранилище объектов добавляется новая задача и обрабатывается событие succes s, кото­
рое генерируется, если добавление прошло успешно. Полу че ние кол ичест ва объ ектов в хранилище Для получения количества объектов в хранилище применяется метод count ( ).Так, в следующем примере запрашивается количество объектов в хранилище объектов tasks: var trans act ion = db.trans act ion ("tasks"); var s t ore = transact i on.obj ectStore ("t asks"); var reqCount = s t ore.count ( ); reqCount.onsuccess = funct ion ( evt ) var count = evt.t arget.result; consol e.log ( count ); } ; Глава 8. Создание источников данных Здесь создается транзакция чтения и с помощью метода coun t ( ) запрашивается количество объектов в хранилище tasks. Отметим, что обращение к методу count ( ) производится асинх­
ронно. Чтобы получить запрошенное количество, нужно обработать событие succe s s и прочитать свойство target. resul t. Выбор ка объекто в из хранилища Для выборки объектов из хранилища необходимо открыть курсор и перемещать его по одному объекту за раз. В примере ниже открыва­
ется курсор, который возвращает объекты из хранилища tasks: var i t ems = [ ]; var transact i on = db.transact i on ("t asks"); var s tore = transact ion.obj ect Store ("t asks"); var req = store.openCursor ( ); req.onsuccess = funct ion ( evt ) { var cursor = evt.target.result; i f ( cursor ) { i t ems.push ( cursor.value ); cursor.cont inue ( ); el s e { 1 1 Готово! Если при открытии курсора не было ошибок, то генерируется со­
бытие succes s. Для получения текущего объекта нужно прочитать свойство curs or. val ue. Метод curs or. continue ( ) продвигает кур­
сор вперед и снова генерирует событие succes s. По достижении конца курсор принимает значение null. Это озна­
чает, что из хранилища выбраны все запрошенные объекты. Примечание. Метод cur s or. cont i nue ( ) продвигает курсор на один объ­
ект. Если требуется продвинуться больше чем на один объект, используйте метод cur sor. ad vance ( ). Ис польз ование индексов и диапазо нов кл ючей При создании базы данных можно создавать индексы по свойст­
вам объектов. Индекс позволяет эффективно извлекать объекты, ключ которых равен заданному значению или попадает в указанный диапазон значений. Ниже показано, как создать в хранилище объек­
тов tas ks индекс по свойству dateCreated: База данных lndexDB как источник данных 111 ••tи1 var reqOpen = window. indexedDB. open ( "TasksDB", 2 ) ; reqOpen.onupgradeneeded = funct i on ( evt ) { var newDB = evt.t arget.resul t; var s t ore=newDB.creat eObj ectStore ("t asks", { keyPath: "id" , autoincrement: true } ) ; store.createindex ("dat eCreatedindex", "dateCreated"); } ; После создания индекса можно извлекать объекты из хранилища, указывая индекс и диапазон ключей. Диапазон ключей представляет критерий сопоставления с объектами в хранилище. Задавая диапазон ключей, можно указывать следующие свойства: only - отбираются только объекты, ключ которых точно сов­
падает с указанным значением; lowerBound - отбираются все объекты, ключ которых больше указанного значения; upperBound - отбираются все объекты, ключ которых меньше указанного значения; bound - отбираются все объекты, ключ которых больше ниж­
него и меньше верхнего из двух указанных значений. Например, вот как можно отобрать из хранилища tasks все зада­
чи, созданные до 1 999 года: var i t ems = [ ]; var trans act i on = db.trans act i on ("t asks"); var s tore = transac t i on.obj ectStore ("t asks"); var index = store.index ("dateCreat edindex"); var keyRange = IDBKeyRange.upperBound ( new Date ("l/1/1 9 9 9") ); var req = index.openCursor ( keyRange ); req.onsuccess = funct ion ( evt ) { var cursor = evt.t arget.resul t; i f ( cursor ) { i t ems.push ( cursor.value ); cursor.cont inue ( ); el s e { // Готово! Индекс можно использовать и для подсчета количества объектов с ключами, попадающими в указанный диапазон. Например, во фрагмен­
те ниже мы запрашиваем, сколько задач было создано до 1 999 года: var trans act i on = db.transact i on ("t asks"); var s t ore = t ransact i on.obj ectStore ("t asks"); ЕШ•• • 1· Глава 8. Создание источников данных var index = s t ore.index ("dat eCreat edindex• J; var keyRange = IDBKeyRange.upperBound ( new Date ("l/1/1 9 9 9") ); var req = index.count ( keyRange ); req.onsuccess = funct i on ( evt ) { var count = evt.target.result; Использование источника данных lndexedDB Источник данных IndexedDB позволяет привязывать элемент управ­
ления ListView к коллекцииJаvаSсгiрt-объектов, хранящихся в базе данных I ndexedDB. Конструктору источника данных IndexedDB пе­
редаются три аргумента: objectStoreName - имя хранилища объектов, представленно­
го источником данных; creationOptions параметры создания базы данных IndexedDB и хранилища объектов; cursorOptions (необязательно) - если этот параметр задан, то объекты в хранилище можно фильтровать с помощью диа­
пазона ключей. Ниже показано, как создать экземпляр источника данных IndexedDB, представляющий коллекцию фильмов: // Задаем параметры источника данных var createOpti ons = { databas eName: "Movi esDB" , databas evers ion: 1, indexNames: [ • c ategory• } ; 1 1 Создаем источник данных IndexedDB var moviesDataSource = new DataSources.IndexedDbDataSource ("movies •, createOpt i ons ); Здесь объект creationOptions представляет свойства базы дан­
ных IndexedDB, хранилища объектов и индексов. Если база данных еще не существует, то источник создаст ее. В объекте creationOption разрешается задавать следующие свойства: dataЬaseName - имя базы данных IвdexedDB; dataЬaseVersion - версия базы данных IвdexedDB; indexNames - для каждого свойства, указанного в этом масси­
ве, создается индекс. База данных l ndexDB как источник данных lll••ВD При создании экземпляра источника данных IndexedDB можно также указать объект cursorOptions, который позволяет фильтро­
вать хранилище. Ниже перечислены свойства этого объекта: indexNarne - создавать диапазон ключей этого индекса; only - создать диапазон ключей и возвращать только объекты, ключ которых совпадает с указанным значением; lowerBound - создать диапазон ключей и возвращать только объекты, ключ которых больше указанного значения; upperBound - создать диапазон ключей и возвращать только объекты, ключ которых меньше указанного значения. Ниже показано, как отфильтровать только задачи, созданные до 1 999 года: WinJS.UI.proces sAl l ( I .done ( funct i on ( ) { var lvTasks = document.getElementByid ("lvTasks") .winControl; var creat eOpti ons = { indexNames: [ "dateCreated" ] , databasevers i on: 3 } ; var curs orOpti ons indexName: "dateCreat ed" upperBound: new Dat e ("l/1/1 9 9 9") } ; var tasksDataSource = new DataSources.IndexedDЬDataSource ("tasks", 4-createOpt i ons, cursorOpti ons ); lvTasks.i t emDataSource = tasksDataSource; } 1; Здесь объект createOptions говорит, что при создании хранили­
ща объектов tasks следует создать индекс по свойству dateCreated. А объект cursorOptions указывает, что при использовании это­
го индекса нужно возвращать только задачи, для которых свойство dateCreated меньше 1/1/1 999. Теперь я хочу продемонстрировать более полный пример работы с источником данных IndexedDB. Приложение Movie (листинги 8.3 и 8.4) позволяет выбрать категорию фильмов и показать фильмы из выбранной категории. Кроме того, приложение позволяет создавать новые фильмы и удалять существующие (рис. 8.6). Ли сти нг 8.3. Приложение Movi e с источником данных l ndexedDB ( indexedDb\indexedDb.html ) <!DOCTYPE html> <html> <head> <met a charset ="ut f - 8" /> ШJ•• • •• Глава 8. Создание источников данных <t i t le>DataSources</t i t l e> <!- - Ссылки на WinJS - - > < l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui -dark.cs s" �re l ="s tyl esheet" /> <script src="//Microsof t.WinJS.1.0/j s/base.j s"></script> <script src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на источники данных - - > < l ink hre f ="indexedDb.cs s" rel ="s tyl esheet" /> <script type ="t ext/j avascript" src ="indexedDbDataSource.j s"> �</s cript > <script src ="indexedDb.j s"></script> </head> <body> <div id="tmplMovi e" data-win-control ="WinJS.Binding.Templ at e"> <div cl as s ="moviei t em"> Id: < span data-win-bind="innerText:id"></span> <br /><br /> Tit l e: <span data-win-bind="innerText:t i t l e"></span> <br /><br /> Category: <span data-win-bind="innerText:cat egory"></span> </div> </div> <div> <l abel > Category </l abel> <s elect id="s el ectcategory"> <opt i on>Al l </opti on> <opt i on>Sc i Fi </opti on> <option>Mus ical</opti on> </sel ect> </div> <div id="lvMovi e s" data-win-control ="WinJS.UI.Li s tView" data-win-opti ons ="{ i t emTemplat e: select ('#tmplMovie'), sel ecti onMode: 's ingl e' }"> </div> < form id="frmAdd"> <f i eldset> <l egend>Add Movi e</legend> <div> <l abel>Ti t le</label > <input id="inputMovi eTi t l e" required /> <l abel >Cat egory</l abel> База данных l ndexDB как источник данных cs el ect id="s el ec tMovi eCat egory"> copt i on>Sc i Fi c/opt i on> copt i on>Mus i calc/opt i on> c/sel ect> cbutt on>Addc/but t on> c/div> </f ieldset> </f orm> cbutton id="btnNuke">Nukec/but t on> cbutton id="btnDel et e">Deletec/button> </body> c/html > ld 1 Тitle r orbl ckk"П �anet C.<1\t.y.tny �tll'-1 l l l ••EШ Рис. 8.6. Использование источника данных l ndexedDB На НТМL-странице из листинга 8.3 присутствует список, из ко­
торого выбирается категория фильмов (All - Все, SciFi - Научная фантастика, Musical - Мюзиклы). После выбора категории в элемен­
те управления Lis tView показываются только принадлежащие ей фильмы. Листинг 8.4. Приложение Movie с источником данных l ndexedDB ( indexedDb\indexedDb.js) /// creference path="//Micros of t.WinJS.1.0/j s/base.j s • /> /// creference path="//Micros of t.WinJS.1.0/j s/ui.j s • /> funct ion init ( ) { WinJS.UI.proces sAl l ( ).done ( func t i on ( ) { Глава 8. Создание источников данных var lvMovies = document.getElementByid ("lvMovi es") .winControl; 1 1 Задаем параметры источника данных var createOpti ons = { databaseName: "MoviesDB", databaseVers i on: 1, indexNames: [ "cat egory" ] }; 1 1 Создаем источник данных IndexedDB var moviesDataSource = new DataSources.IndexedDbDataSource ("movies", createOptions ); 1 1 Помещаем начальные данные addSeedDat a ( ) .done ( funct i on ( ) 1 1 Привязываем источник данных к Li s tVi ew lvMovi es.i t emDat aSource = moviesDataSource; } ) ; funct i on addSeedDat a ( ) return new WinJS.Promi s e ( funct i on ( compl et e ) moviesDat aSource.getCount ( ) .then ( funct i on ( count ) i f ( count > 0 ) { } } ) ; } ) ; compl ete ( ); e l s e { var s eedData = [ ] ; t i t l e: "St ar Wars", category: "Sci Fi" } , t i t l e: "Forbidden Pl anet", cat egory: "Sci Fi" } , t i t l e: "Show Boat", category: "Mus i cal" } var promi s es = [ ]; s eedData.f orEach ( func t i on ( data ) promises.push ( moviesDataSource.insertAtEnd ( пul l, data ) ); } ) ; WinJS.Promi se.j oin ( promi s es ) .done ( funct i on ( ) complete ( ); } ) ; 1 1 Настроить список SelectCategory и кнопки Add, Del et e, Nuke document.getElementВyid ("selectCategory") .addEventListener ("change", '+funct i on ( evt ) { var category = document.getElementByid ("selectCategory") .value; i f ( category === "Al l") { moviesDataSource = new DataSources.IndexedDbDataSource ("movies", '+createOpti ons ); } el s e { var curs orOpti ons = { indexName: "category" , База данных l ndexDB как источник данных only: document.getElementByid ("s el ectCat egory") .value } ; moviesDataSource = new DataSources.IndexedDbDataSource ("movies", '+createOpt i ons, curs orOpti ons ); } lvMovi es.i t emDataSource = movi esDataSource; } ) ; document.getElementByid ("f rmAdd") .addEventLi s t ener ("submit", '+func t i on ( evt ) { evt.preventDefaul t ( ); moviesDataSource.beginEdi t s ( ); moviesDataSource.insertAtEnd ( nul l, t i t l e: document.getElementByi d ("inputMovieTi t l e") .value, cat egory: document.getElementByid ("se lectMovi eCategory"). '+value } ) .done ( funct i on ( newit em) moviesDataSource.endEdi t s ( ); document.getElementByid ("frmAdd") .reset ( ); lvMovi es.ensureVi s iЬle ( newi tem.index); } ) ; } ) ; document.getElementByid ("btnDel et e") .addEventLi s tener ("c l ick", '+func t i on ( ) { i f ( lvMovies.sel ecti on.count ( ) == 1 ) moviesDataSource.beginEdi t s ( ); lvMovi es.s el ect i on.get i t ems ( ) .done ( func t i on ( i t ems ) moviesDat aSource.remove ( i t ems [ O ] .key); moviesDataSource.endEdi t s ( ); } ) ; } } ) ; document. getElementByid ( "btnNuke") . addEventLi s t ener ( "c l ick", '+funct i on ( ) { moviesDataSource.nuke ( ); } ) ; } ) ; document.addEventLi s t ener ("DOMContentLoaded", init ); В листинге 8.4 приведен JavaScript-кoд приложения Movie. При первом открытии этого приложения создается источник данных IndexedDB без задания параметра curs orOption, и отображаются все объекты, находящиеся в хранилище: 1 1 Создаем источник данных IndexedDB var moviesDataSource = new DataSources.IndexedDbDataSource ("movies", createOpti ons ); 1 1 Помещаем начальные данные addSeedDat a ( ) .done ( funct i on ( ) { Глава 8. Создание источников данных 1 1 Привязываем источник данных к Li s tView lvMovi es.i t emDataSource = movi esDat aSource; } ) ; Обратите внимание на обращение к методу addSeedData ( ).Этот метод помещает начальные данные в базу данных IndexedDB, чтобы она не была пустой при первом запуске приложения Movie. При выборе категории из списка выполняется следующий код: document.getElementByid ("selectCategory") .addEventListener ("change", '+functi on ( evt ) { var category = document.getElementByi d ("selectCategory") .value; i f ( cat egory === "Al l") { moviesDataSource = new DataSources.IndexedDbDataSource ("movies", '+createOpti ons ) ; } el s e { var curs orOpt i ons = { indexName: "c ategory", only: document.getElementByid ("s electCategory") .value } ; moviesDataSource = new DataSources.IndexedDbDataSource ("movies", '+createOpt i ons, cursorOpti ons ); } lvMovi es.i t emDat aSource = moviesDat aSource; Здесь объект cursorOption используется для создания нового источника IndexedDB, который представляет только подмножество объектов из хранилища movies. Р ез юме В этой главе мы занимались созданием нестандартных источников данных, пригодных для использования совместно с элементами уп­
равления из библиотеки WinJS. Вы научились писать классы источ­
ников данных, производные от Virtuali zedDataSource. Я продемонстрировал три источника данных. Сначала я создал файловый источник, который позволяет извлекать и записывать дан­
ные в файловую систему вашего компьютера. Затем я создал источ­
ник данных, ассоциированный с удаленной веб-службой. И наконец, мы рассмотрели создание источника данных, работающего с базой данных I11dexedDB. - -
rЯАВА 9. События и состоя ния nр иnо ж ения В этой �лаве: • События приложения.
• Приостановка, завершение и возобновление прило­
жения.
• Состояния просмотра приложения.
В этой главе мы рассмотрим события, жизненный цикл и состояния просмотра приложения Магазина Windows. В первой части я расска­
жу о стандартной последовательности событий, возникающих после запуска приложения Магазина Windows. Вы узнаете, как обрабаты­
вать события загрузки, активации и готовности. Далее я объясню, как обрабатывать события приостановки, за­
вершения и возобновление приложения. Вы научитесь сохранять и восстанавливать состояние приложение, создавая у пользователя ил­
люзию, будто приложение работает постоянно. Наконец, мы поговорим о состояниях просмотра приложения. Вы узнаете, как переключаться между прикрепленным, заполняющим и полноэкранным состояниями и как определить текущее состояние с помощью опроса носителя в CSS иjavaScгipt. Со бытия приложе ния При запуске приложения Магазина Windows возникают следующие события в указанном порядке: ШJ• • • •tт Глава 9. События и состояния приложения WinJS. Applica tion. loaded - генерируется в результате стан­
дартного события браузера DOMContentLoaded, сразу после окончания загрузки НТМL-документа; WinJS .Appl ication. activated - генерируется, когда прило­
жение становится активно; WinJS. Appl i ca tion. ready - генерируется после событий loaded и activated; WinJS. Applica tion. unload - генерируется в результате стан­
дартного события браузера beforeunload, непосредственно перед выгрузкой страницы. Можно также обработать и другие события приложения, генери-
руемые в ответ на различные условия: WinJS .Appli cation. error - генерируется в случае, когда в приложении имеет место необработанное исключение; WinJS .Appli cation. checkpoint - генерируется, когда при­
ложение приостанавливается; WinJS .Appli cation. s ettings - генерируется, когда изменя­
ются настройки приложения. Ни одно из этих событий не возникнет, если не был вызван метод WinJS. Applica tion. s tart ( ). До этого момента события ставятся в очередь, но не генерируются. J1JvziSпipt Consor� Х c1ear [Ciol:rror;i !.Aow�;�;���j О 2 мешgеs loarl'e:d ac.ti\•ated гet11dy Рис. 9.1. Протоколирование событий приложения Магазина Wi ndows В следующем фрагменте показано, как создать обработчики собы­
тий, которые будут выводить на консоль JavaScгipt в Visual Studio сообщение о том, что событие произошло. WinJS.Appl i cat i on.addEventLi s t ener ("l oaded", funct i on ( evt ) { cons ole.l og ("l oaded"); } ) ; WinJS.Appl i cat i on.addEventLi s t ener ("act ivated", funct i on ( evt ) { cons ole.log ("act ivated"); } ) ; . События приложения WinJS.Appl icat ion.addEventLi s t ener ("ready", funct i on ( evt ) { cons ol e. log ( "ready") ; } ) ; Обработка события activated Событие acti va ted возникает, когда приложение Магазина Windows становится активным. Активировать приложение можно нескольки­
ми способами, и свойство ActivationKind говорит о том, как именно произошла активация: WinJS.Appl i cat ion.addEventLi s t ener ("act ivat ed", funct i on ( evt ) var act ivat i onKind = Windows.Appl i cat i onModel.Act ivat ion. '+Act ivat i onKind; swi tch ( evt.detai l.kind ) { } } ) ; case act ivati onKind.l aunch: cons ol e.l og ("Зaпyщeнo из плитки"); break; case act ivat i onKind.s earch: соns оl е.l оg ("Активировано в результате поиска"); break; de f aul t: cons ol e. l og ("Активировано по иной причине" ) ; Самый типичный способ активации приложения Магазина Windows - щелчок мышью по плитке приложения на начальном эк­
ране. В таком случае свойство ActivationKind принимает значение launch. Но существуют и другие способы активации. Например, прило­
жение может быть активировано в ответ на операцию поиска из чудо­
кнопки Search ( Поиск) или потому что пользователь предоставил к чему-то общий доступ из чудо-кнопки Shaгe ( Общий доступ) или потому что он щелкнул по какому-то файлу в другом приложении, например в почтовом клиенте. В любом случае свойство Ас t i v а t i on -
Kind позволит определить причину активации. Обработка события error По умолчанию, если в приложении Магазина Windows возникает необработанное исключение - ошибка, не перехваченная в блоке tiy .. catch, - происходит следующее: 1. Сообщение об ошибке протоколируется путем вызова метода WinJS. log ( ). 1Ш••• 1 Глава 9. События и состояния приложения 2. Если приложение работает в отладочном режиме в Visual Studio, то выполняется предложение debugger, которое вызы­
вает отладчик. 3. Приложение завершается путем вызова метода MSApp. terminateApp ( ). Результатом работы метода terminateApp ( ) является немедлен­
ный переход на начальный экран Windows (неожиданный и без объ­
яснения причин). Чтобы не раздражать пользователя, вы можете обработать собы­
тие приложения error. Ниже показано, как вывести в этом случае сообщение об ошибке. WinJS.Appl icat i on.addEventLi s t ener ("error", funct i on ( evt } { var mes s age = new Windows.UI.Popups.Mes s ageDialog ( 11 There was an error." } ; mes s age.showAsync ( ); return true ; } ) ; WinJS.Appl ication.addEventLi s t ener ("ready", funct i on ( ) { throw new WinJS.ErrorFromName ("MyError", "Yikes! An Error!"); } ) ; WinJS.Appl icat i on.s tart ( ); В результате исполнения этого кода появится диалоговое окно с сообщением «Тhеге was an епог� (рис. 9.2). Рис. 9.2. Вывод сообщения об ошибке Отметим, что обработчик ошибки возвращает значение true. Тем самым он говорит системе о том, что ошибка обработана и завершать приложение не нужно. Если вы хотите завершить приложение после исполнения своего обработчика ошибки, не возвращайте true. Предупреждение. Если ошибка произойдет до выполнения метода WinJS. appl ication. s tart ( ), то написанный вам обработчик ошибки не вызыва­
ется. События приложения ••tш Откладывание событий с помощью обещаний События WinJS .Appl i cation отличаются от обычных событий DOM тем, что поддерживают обещания. Вы можете выполнить в об­
работчике события WinJS. Appli cation какую-нибудь асинхронную операцию и отложить генерацию следующего события до момента исполнения обещания. Например, в файле de f а и 1 t . j s, который автоматически создает­
ся при создании проекта приложения Магазина Windows обработка события WinJS .Appl ication. activated откладывается до момента завершения асинхронного вызова метода WinJS. UI .proces sAll ( ). Тем самым гарантируется, что все элементы управления на странице будет обработаны до убирания заставки. Вот как выглядит обработчик события WinJS .Appl i cation. activated в файле default. j s (часть кода опущена): app.onact ivated = funct i on ( args ) { i f ( args.detai l.kind === act ivat i on.Ac t ivat i onKind.launch) args.setPromi s e ( WinJS.UI.proces sAl l ( ) ); } } ; Здесь метод args. setPromi s e ( ) используется для вызова метода proces sAll ( ),который возвращает обещание. Этот метод выполня­
ется асинхронно, и событие ready возникает только после того, как он завершится. Создание нестандартных событий Можно создавать и прослушивать свои собственные события - лю­
бого типа. Например, в игре имеет смысл создать событие периодического сигнала, которое генерируется один раз в секунду и служит для об­
новления игрового поля. Ниже демонстрируется, как генерировать и прослушивать подобное событие: window.set int erval ( funct i on ( ) { WinJS.Appl i cat i on.queueEvent ( { type: "heartbeat" } ); } ' 1 0 0 0 ); WinJS.Appl icat i on.addEventLi s t ener ("heartbeat", funct i on ( evt ) { consol e.log ("heartbeat"); } ) ; WinJS.Appl icat i on.s tart ( ); ШJ • • •• • Глава 9. События и состояния приложения Здесь мы пользуемся методом s et interval ( ), чтобы настроить выполнение кода один раз в секунду. Метод queueEvent ( ) ставит со­
бытие периодического сигнала в очередь. Для прослушивания события используется метод addEventLi s ­
tener ( ). Каждую секунду в окно консоли JavaScгipt в Visual Studio выводится сообщение «l1eaгtbeat». При желании можно сопроводить событие дополнительной ин­
формацией, передав методу queueEvent аргумент detail:
WinJS.Appl i cat i on.queueEvent ( {
type: "heartbeat",
detai l: { numberOf Pl ayers: 1 2 } ) ; Приостановка, з авершение и возо бновление прилож ения Приложение Магазина Windows должно поддерживать иллюзию, будто оно работает постоянно, хотя пользователь может судорож­
но переключаться между несколькими открытыми приложениями. Как правило, приложение Магазина Windows не закрывается явно, а просто происходит переход к другому приложению. При переходе к другому приложению Windows приостанавливает текущее приложение, но оставляет его в памяти. Если в результате работы других приложений возникнет нехватка памяти, то Windows «молча» завершит приложение. С точки зрения пользователя, приложение должно вести себя одинаково вне зависимости от того, было оно приостановлено или завершено. Пользователь ожидает, что приложение будет находиться в том же состоянии, в каком он его оставил, даже его вернется к нему спустя несколько часов. Например, если пользователь читал какую­
то новость, то в момент возврата в приложение должна быть выбрана именно эта новость. Определение того, что приложение приостановлено или завершено Определить, что приложение приостановлено, можно, обработав со­
бытие WinJS .Appl i cation. checkpoint. Его обработчик должен со­
хранить состояние приложения на случай, если система завершит его Приостановка, завершение и возобновление .• •
111 ••Ш1 после приостановки, чтобы при повторном запуске приложения это состояние можно было восстановить. Приложение Магазина Windows никаким способом не может узнать, что оно завершено. В частности, не существует события terminated. Поэтому, если требуется сохранять состояние приложе­
ния для последующего перезапуска, то делать это нужно в обработчи­
ке события checkpoint.
Ниже показана схема обработки этого события: WinJS.Appl i cat i on.addEventLi s t ener ("checkpoint", funct i on ( ) { 1 1 Сохранить состояние приложения } ) ; Определение предыдущего состояния выполнения Когда приложение Магазина Windows активируется, можно восполь­
зоваться свойством previousExecutionState, чтобы узнать, было ли оно только что запущено, ранее закрыто пользователем или возоб­
новлено после приостановки и завершения. В зависимости от значения previousExecutionState можно за­
грузить состояние по умолчанию или ранее сохраненное. Например, в приложении для чтения новостей, вероятно, имеет смысл загрузить новость, которую пользователь читал ранее. С другой стороны, если приложение ранее не запускалось, то можно загрузить страницу по умолчанию, например начальную страницу приложения. Вот как может выглядеть код для проверки предыдущего состоя­
ния: WinJS.Appl icat i on.addEventLi s t ener ("act ivat ed", funct i on ( evt ) { var appState = Windows.Appl icationМodel.Act ivat ion.Appl icat ionExecutionStat e; i f ( evt.detai l.previousExecut i onState == appState.notRunning ) 1 1 Приложение не з апускалось в текущем сеансе пользователя 1 1 или завершилось аварийно. 1 1 Загрузить состояние по умолчанию. i f ( evt.detai l.previousExecut ionState == appState.closedByUser ) 1 1 Приложение было закрыто пользователем. 1 1 Загрузить состояние по умолчанию. i f ( evt.detai l.previousExecuti onState == appStat e.t erminated) 1 1 Приложение было приостановлено и завершено. } } ) ; 1 1 Восстановить предыдущее состояние. EШ••ll� Глава 9. События и состояния приложения Вэтомкоде представленобработчиксобытияwinJS .Application. activated. В свойстве event.previousExecutionState хранится предыдущее состояние приложения, описываемое перечислением ApplicationExecutionState. Ниже перечислены все элементы это­
го перечисления. notRunning - пользователь активирует приложение после его установки из Магазина Windows, перезагрузки компьютера, выхода и последующего входа в систему, завершения задачи, аварийного завершения приложения или закрытия приложе­
ния и перезапуска в течение 1 О секунд с момента закрытия. running - приложение уже работает, и пользователь активи­
рует его с помощью вспомогательной плитки или одного из контрактов активации или расширений. suspended - приложение приостановлено и активировано с помощью вспомогательной плитки или одного из контрактов активации или расширений. terminated - приложение было завершено Windows. closedВyUser - приложение было закрыто пользователем и не перезапущено в течение по крайней мере 1 О секунд. Примечание. Невозможно узнать, было ли приложение активировано пос­
ле приостановки. Можно только определить, что приложения активировано после приостановки и завершения. Событие act i vat ed не генерируется для приложений, которые были приостановлены, но не завершались. Тестирование состояния приложения в Visua/ Studio Для тестирования поведения приложения Магазина Windows в слу­
чае приостановки, завершения и возобновления можно воспользо­
ваться Visual Studio. При запуске приложения из Visual Studio на экране присутствует раскрывающийся список (на панели инструмен­
тов Debug Location - Место отладки), показанный на рис. 9.3. Чтобы имитировать приостановку и возобновление приложения, выберите сначала команду Suspe11d (Приостановить), а затем Resume ( Возобновить). При выборе команды Suspe11d генерируется событие checkpoint. При выборе команды Resume событие activated не ге­
нерируется (приложение не знает, что оно было возобновлено после приостановки). Чтобы имитировать приостановку и завершение приложения, вы­
берите команду Suspend and shutdovvn ( Приостановить и завершить). Приостановка, завершение и возобновление ... 111••ШJ В этом случае генерируется событие checkpoin t и приложение завер­
шается. При повторном запуске приложения в Visual Studio свойство previousExecutionState будет иметь значение terminated.
��ff:ij'ltt:; f..i;� ilt,;; :0."l!�t.'"·(� А�р!>О!:>.1)" i.ppH•l•C-"i Рис. 9.3. Изменение состояния приложение Хранение состояния приложения в состоянии сеанса Если приложение просто приостановлено, то его состояние не те­
ряется. Если же приложение приостановлено, а затем завершено, то состояние теряется. При повторном запуске приложения значения внутренних переменных будут инициализированы заново. Существует специальный объект WinJS .Application. s es s ion­
State (состояние сеанса), который можно использовать для запо­
минания состояния в случае приостановки приложения. Всё поме­
щенное в состояние сеанса остается доступным и после перезапуска приложения. В примере ниже показано, как воспользоваться состоянием сеанса для сохранения текущего счета игры: ( funct i on ( ) { 11 use strict lf ; var _gameScore; WinJS.Appl ication.addEventListener ("act ivated", functi on ( evt ) { Глава 9. События и состояния приложения var appState = Windows.Appl i cat i onModel.Act ivat i on. '+Appl i cat i onExecut ionState; i f ( evt.detai l.previousExecutionState == appState.notRunning 1 1
evt.detai l.previousExecut i onState = = appStat e. '+cl osedByUser ) { 1 1 Установить счет игры по умолчанию _gameScore = О; i f ( evt.detai l.previousExecut ionState == appState.terminated) 1 1 Загрузить счет игры из состояния сеанса } } ) ; _gameScore = WinJS.Appl icat i on.s es s i onState.gameScore; WinJS.Appl i cat i on.addEventLi s t ener ("checkpoint", funct i on ( ) { 1 1 Сохранить счет игры в состоянии сеанса WinJS.Appl icat ion.ses s i onStat e.gameScore = _gameScore; } ) ; WinJS.Appl i cat i on.addEventLi s t ener ("ready", funct i on ( evt ) { 1 1 Враг убит, + 1 к счету игры document.getElementByid ("btnКillAlien") .addEventListener ("click", '+funct i on ( ) { _gameScore++; } ) ; } ) ; WinJS.Appl ication.s tart ( );
} ) ( ); В этом коде имеется три обработчика событий. click - при нажатии кнопки ЬtnKi l lAl ien к счету игры при­
бавляется единица. checkpoin t - вызывается в случае приостановки приложения и сохраняет текущий счет игры в состоянии сеанса. activated - вызывается в случае активации приложения. Если приложение активировано после завершения, то счет игры загружается из состояния сеанса, в противном случае инициализируется по умолчанию. Состояние сеанса загружается только в случае активации после завершения. Оно не сохраняется, к примеру, в ситуации, когда поль­
зователь явно закрывает приложение или перезагружает компьютер. Примечание. В действительности состояние сеанса хранится в локальном
файле с именем _s e s s i o nSt at e. j son. После события check poi nt состоя­
ние автоматически записывается в этот файл, а при активации после завер­
шения - автоматически считывается из него. Состояния просмотра приложения Состоя ния прос мот ра приложения Приложение Магазина Windows может отображаться по-разному в зависимости от состояния просмотра. В разных состояниях приложе­
нию доступны различные по высоте и ширине области экрана. Правильно спроектированное приложение Магазина Windows должно адаптироваться к выделенному ему месту на экране. Напри­
мер, если <:< широкое» приложение принудительно «сужается», то лишнее содержание необходимо автоматически скрыть. В этом разделе мы рассмотрим различные состояния просмотра приложения Магазина Windows и научимся использовать опросы но­
сителя для адаптации к текущему состоянию. Прикрепленное и заполняющее, альбомное и книжное Приложение Магазина Windows может находиться в прикрепленном, заполняющем или полноэкранном состоянии просмотра. Каждому состоянию соответствует некоторая ширина занимаемой области. Например, одно приложение может быть прикреплено к левому или правому краю экрана, а другое заполнять оставшуюся часть. Говорят, что первое приложение находится в прикрепленном сост оянии прос­
мот ра, а второе - в заполняющем. На рис. 9.4 показано различие между прикрепленным и заполня­
ющим состоянием - Inteгnet Ехрlогег находится в заполняющем, а приложение Weatheг - в прикрепленном состоянии. iEl1r�t\t• jJorkёlm" " :iiiiii:·�:·.·;:;:;;·;���-�-�т�;;;::;д-i;;;.,i···:т;;�····:·::·::·::···;·� '" . .::····· Рис. 9.4. Прикрепленное и заполняющее состоя ние просмотра Глава 9. События и состояния приложения Предупреждение. Переключиться в прикрепленное состояние можно толь­
ко в том случае, когда разрешение устройства по горизонтали не менее 1 366 пикселей. В противном случае на экране не хватит места для показа несколь­
ких приложений. Для циклической смены состояния текущего приложения - при­
крепленное к левому краю, прикрепленное к правому краю, полноэк­
ранное - служит комбинация клавиш Win +.(точка). При наличии сенсорного экрана той же цели можно достичь, медленно буксируя приложение от левого края экрана. Ширина прикрепленного приложения всегда равна 320 пикселей. Разделитель забирает еще 22 пикселя. Ширина заполняющего при­
ложения зависит от разрешения экрана, но никак не менее 1 024 пик­
селей. См. рис. 9.5. 1 ,024 пикселей Рис. 9.5. Размеры прикрепленного приложения Приложение Магазина Windows должно правильно показываться в книжной и альбомной ориентации. Поворачивая свой планшет, я ожидаю, что открытое приложение автоматически перейдет из книж­
ной ориентации в альбомную и наоборот (рис. 9.6 и 9.7). Правильно спроектированное приложение Магазина Windows должно хорошо выглядеть в любом состоянии просмотра - полноэк­
ранном, прикрепленном или заполняющем - и при любоl'r ориента­
ции - книжной или альбомной. Состояния просмотра приложения " a..�;;.�44 � _-f ik'c �-.� -��� i4ik�1м?tf ik�1�;;«i--%ifLOi�� i{�{�,���д�i�йi:0�1 ) . \it!�:Ь.i�;;;;:;i l'J �11·,.......,...;-�.-.... S·��<'"•"- � -"'� ""'""-"'-.."«-·· •'I•"'•" : .... _,,... :=:�r:;�*; '"'"""-'"'t:<IJ� '�� .. ,,.,"."" .. " .. J.Jt·oнi;:ltt f •:i 1Jri,,..,··t!p ·1�.",н·I Pt·t\:,,r. r-;, •• ""1 -��-�-�1·, -��.s. :;:�,s� ·r: .... ..,,.,"�ы...,�·� :i:11 ... ........ !)')"•'• М.:.� •);<11 f�!l!'#'"!l � IЩ"ll"f l"';\/'t<i · Щ< :.(.··.,.;�•<("t!YO� .• ._C\,oo;I! ,ь;n-,o":t..:<r-1:::1-.!.•�tt �;;��:::;-1��:"' Р""'° < t ... 'f'>•I O<.:.<"'°"''J'!'<:oMIA'•'°< <t<:l <,>11<•,'<."'"'''""' •\1,"" •\•· •�11-.... �,,,, """� ..... ," \< ."."���·.><.•. _.,,". М:1�,...,.,Ф-t<!.»><>;о!'14 ;.,...щ".,;;.,,",�,::: ... J:;. ;����,:61;?; ''"'""''"'-"''•"''" • -�� <"·'•'- J'"•.;:;,.. Рис. 9.6. Аль бомная ориентация i!!l1r �t\V !Jork i!!imrs ��·1����:.=';., � .11 т�_r:�� �·''1:.'t.C::, Р.11""' >l•l<ro �i�� ���r�;:;. �:1::". �Ё:Е��::.::�-�:;; '.':'.:}\��..t"i ..... ...::.<:-:-"" ............ ,'-.... �-
§:�·�:�tт����-
Рис. 9. 7. Книжная ориентация ЕШ• • 1 1· Глава 9. События и состояния приложения Опрос носителя Для автоматической адаптации приложения к состоянию просмотра можно воспользоваться опросо:м носит еля в сочетании с каскадными таблицами стилей. Опрос носителя позволяет определить характе­
ристики устройства и соответственно изменить представление со­
держимого. Примечание. Опрос носителя - рекомендация WЗС. Использовать этот ме­
ханизм можно не только в приложениях Магазина Wi ndows, но и на обычных веб-сайтах. Опрос носителя поддерживают браузеры Googl e Chrome 4+, Mozi l l a Fi refox 3.5+, Appl e Safari 4+ и Mi crosoft l nterпet Expl orer 9+. Допустим, требуется показывать различное содержимое в зави­
симости от горизонтального разрешения устройства. В таком случае можно сгруппировать стили с помощью правила @шedia. Например, следующая НТМL-страница содержит три набора сти­
листических правил. Один относится ко всем устройствам, второй -
к устройствам со средней разрешающей способностью, а третий - к устройствам с высокой разрешающей способностью. <!DOCTYPE html > <html > <head> <ti tl e></t i t l e> <s tyle type ="t ext/c s s"> /* Стили по умолчанию */ .di splayiпMedium, .di spl ayiпHigh display : попе ; /* Больше или равно 1 3 6 6рх */ @media screeп апd ( miп-width .di spl ayiпMedium { display : Ыосk ; } /* Больше или равно 1 9 2 0рх */ 1 3 б брх) { @medi a screeп and ( min-width : 1 9 2 0рх) . displayinMedium , . displayinHigh { display : Ыосk ; </style> </head> <body> <div> <hl >Меня видно при любом разрешении.</hl> Состояния просмотра приложения </div> <div c l as s ="di spl ayinMedium"> <hl >Меня видно при среднем разрешении.</hl > </div> <div c l as s ="displayinHigh"> <hl >Меня видно при высоком разрешении.</hl> </div> </body> </html > Для имитации устройств разного размера можно запустить при­
ложение Магазина Windows во встроенном в Visual Studio эмуляторе и выбрать нужное разрешение (рис. 9.8). В зависимости от разреше­
ния будет показываться содержимое того или иного элемента DIV. Рис. 9.8. Изменение разрешения экрана в эмуляторе Предупреждение. Не все компьютеры поддерживают все разрешения, перечисленные в эмуляторе. Например, на своем ноутбуке я могу переклю­
читься в режим 1 0.6" 2560х1 440, но не в режим 27" 2560х1 440. Майкрософт добавила собственные расширения в механизм оп­
роса носителя. СSS-файл, включаемый по умолчанию в новое при­
ложение Магазина Windows (он находится в папке css\default.css), содержит такие правила: @media s creen and ( -ms-view- state ful l s creen- lands cape ) @media screen and ( -ms-view- state f i l l edJ { @medi a screen and ( -ms -view- state snapped) { @media screen and ( -ms-view- state ful l screen-portrait ) ШJ •• · · � Глава 9. События и состояния приложения Правило -ms -view- s tate позволяет, определить, в каком режи­
ме просматривается страница: полноэкранном (книжном или аль­
бомном), прикрепленном или заполняющем. Таким образом, при переключении состояния просмотра можно автоматически пока­
зать одно содержимое и скрыть другое ( с помощью стиля display: none). Использование метода mediaMatch в JavaScript В предыдущем разделе мы научились опрашивать носитель с помо­
щью каскадных таблиц стилей. Но это можно сделать и в коде на JavaSc1·ipt, воспользовавшись методом window .matchMedia ( ). Этот метод возвращает объект MediaQueryLi st, у которого имеется свойст­
во matches, представляющее успех или неудачу. Например, в следующем фрагменте проверяется, находится ли устройство в книжной ориентации. i f ( window.matchMedi a ("( ori entat i on:portrai t )") .matches ) consol e.log ("portrai t"); el s e { console.log ("l ands cape"); Если свойство matches равно true, то ориентация устройства книжная и на консоли] avaSciipt в Visual Studio печатается сообщение «poгtiait1>, в противном случае - сообщение «la11dscape1>. Можно написать прослушиватель события, срабатывающий, ког­
да результат опроса носителя изменяется. Например, следующий код выводит сообщение на консоль J avaScгipt всякий раз, как устройство переходит в книжный режим. window.matchMedi a ("( ori entat i on:portrai t )") .addLi s t ener <+ ( funct i on ( mql ) { i f ( mql.matches ) { } } ) ; cons ol e. log ("Установлен книжный режим") ; Имейте в виду, что прослушиватель этого события срабатывает при любом изменении результата опроса носителя, то есть при сме­
не альбомной ориентации на книжную и наоборот. Поэтому перед печатью сообщения мы и проверяем, что свойство matches равно true. Состояния просмотра приложения Определение окна просмотра Правилом @ -ms -viewport можно воспользоваться для автоматичес­
кого масштабирования содержимого под различное разрешение уст­
ройства. Оно полезно и для того, чтобы масштабировать содержимое в соответствии с состоянием просмотра: прикрепленное, заполняю­
щее и т. п. Правило @ -ms -viewport позволяет определить окно просмотра, то есть ширину и высоту области экрана, доступной для отображения содержимого независимо от фактического размера экрана. Например, на следующей странице отображается текст и фотогра­
фии Теслы: <!DOCTYPE html> <html> <head> <t i t l e>vi ewport </t i t l e> </head> <body> <hl>A fast, red Tes l a</hl > < img src =" .. /images/t es l a.j pg" /> </body> </html > Если просматривать эту страницу в полноэкранном режиме (рис. 9.9), то все выглядит замечательно. Но в прикрепленном режи­
ме изображение обрезается (рис. 9.1 0). Рис. 9.9. Страница в полноэкранном режиме ED••BI Глава 9. События и состояния приложения А fast, 1·e<I Tesla Рис. 9. 1 О. Страница в прикрепленном режиме А вот как с помощью правила @ -ms -viewport добиться автомати­
ческого масштабирования: <!DOCTYPE html > <html > <head> <t i t le>viewport </t i t le> <s tyle type ="t ext/c s s"> @ -ms-vi ewport { width: 1 0 2 4рх ; } </s tyle> </head> <body> <hl> А f ast, red Tes l a </hl > < img src =" .. /images/t es l a.j pg• /> </body> </html > В данном случае установлено окно просмотра размером 1 024рх х 768рх. Если страница просматривается при меньшем разре­
шении, то ее содержимое уменьшается (рис. 9.1 1 ). Если же страница просматривается при большем разрешении, то ее содержимое (включая и картинку) увеличивается (рис. 9.1 2). Состояния просмотра приложения •••••ШJ Рис. 9. 1 1. В прикрепленном режиме страница уменьшена Л fast, refl Tl'sla Рис. 9. 1 2. В полноэкранном режиме страница увеличена ЕШ• • · · � Глава 9. События и состояния приложения Если требуется задавать окно просмотра (масштабирование стра­
ницы) только в прикрепленном режиме, то можно воспользоваться правилом @ -ms -viewport в сочетании с описанным выше механиз­
мом опроса носителя -ms -view- s tate. Например, следующие СSS­
правила изменяют размер окна просмотра, только когда приложение находится в прикрепленном режиме: @media screen and ( -ms -vi ew- s tate: snapped) { @-ms -viewport { width: 1 0 2 4рх ; } Примечание. Правило @-ms-vi ewpo r t - добавленное Майкрософт рас­
ширение каскадных таблиц стилей, которое основано на ожидаемом правиле @vi ewport в официальной рекомендации WЗС. Предварительная специфика­
ция правила @vi ewpor t вошла в документ «CSS Devi ce Adaptati on», который можно найти no адресу http://dev.wЗ.org/csswg/css-devi ce-adapt/. Рез юме Эта глава была посвящена событиям и состоянию просмотра прило­
жений Магазина Windows. В первой части я рассказал о стандарт­
ных событиях, возникающих во время работы приложения Магазина Windows. Вы узнали, как обрабатывать события загрузки, активации и готовности. Далее мы обсудили вопрос об автоматической приостановке и за­
вершении приложений Магазина Windows в ОС Windows 8. Вы на­
учились использовать состояние сеанса для сохранения и восстанов­
ления данных приложения. Наконец, вы узнали о состояниях просмотра приложения, напри­
мер прикрепленном и заполняющем. Я показал, как воспользоваться механизмом опроса носителя - из CSS и из JavaScгipt - для опреде­
ления текущего состояния просмотра и показа различного содержи­
мого. В этой главе: rЯА В А 10. Фр а rм е нты стран и ц и на виr а ция • Элемент управления Html Contгol.
• Создание страничного элемента управления.
• Создание многостраничных приложений.
Цель этой главы - рассказать об использовании трех элементов уп­
равления: HtmlControl, Page и PageControlNavigator. Все три поз­
воляют отображать содержимое одной НТМL-страницы внутри дру­
гой с помощью Аj ах-запросов. Элемент HtmlControl дает возможность добавить на страницу фрагмент НТМL-кода. Он полезен, когда нужно повторно использо­
вать один и тот же НТМL-код в разных местах приложения. Элементы управления Page позволяют без труда создавать новые элементы управления WinJS из НТМL-страницы,JаvаSсгiрt-файла и таблицы стилей. Их имеет смысл использовать, когда требуется ин­
капсулировать в новом элементе разметку и поведение. Наконец, элемент PageControlNavigator позволяет строить од­
ностраничные приложения, содержащие несколько страниц. Его на­
значение - загружать элементы управления Page, имитируя перехо­
ды между страницами. Элеме нт управле ния HtmlContr ol Если вы хотите включить один и тот же фрагмент НТМL-кода в не­
сколько страниц приложения Магазина Windows или разбить сущ ест-
Глава 1 О. Фрагменты страниц и навигация вующую страницу на меньшие части для удобства работы, то самое время прибегнуть к элементу управления WinJS HtmlControl. Для него требуется всего один параметр: URI, указывающий на подлежа­
щее загрузке содержимое. Примечание. Рассматриваемый в этом разделе код находится в папке Chapter1 0\Fragmeпts проекта на сайте Gi tHub. Пусть требуется показать страницу, в которой есть форма для вво­
да адресов для выставления счетов и доставки. Иными словами, адрес требуется показать дважды (рис. 1 0.1 ). Рис. 1 0.1. Использование элемента управления HtmlControl для ввода адресов В таком случае можно создать форму для ввода адреса в отдельном НТМL-файле addI"essFoгm.11tml: <div> <div cl as s ="l abel"> <l abel for="inpStreet"> Street: </l abel > </div> <div c l as s ="f i el d"> <input c l as s ="inpStreet" required /> </div> </div> <div> <div cl as s ="l abe l"> <l abel f or=• inpCi ty"> Ci ty: </l abel> </div> <div cl as s ="f ield"> <input c l as s ="inpCi ty" required /> </div> </div> Элемент управления Html Control lll••EID Примечание. Обратите внимание, что для полей формы я пользуюсь име­
нами классов, а не идентификаторами, чтобы избежать многократного появ­
ления одного и того идентификатора. Теперь этот фрагмент формы можно дважды включить в страницу с помощью элемента управления HtmlControl:
<hl >Order Form</hl > < form i d="frmOrder"> < f i el ds et> <l egend>Bi l l ing Addres s </l egend> <div id="divBi l l ingAddres s" data-win-control ="WinJS.UI.HtmlContro l" data-win-opt i ons ="{ uri: 'addres s.html' }"></div> </f i eldset > < f i eldset> < legend>Shipping Addres s </l egend> <div i d="divShippingAddres s" data-win-control ="WinJS.UI.HtmlControl" dat a-win-opt i ons ="{ uri: 'addres s.html' } "></div> </f i eldset> < input type="submit" value ="Submit Order" /> </form> А чтобы обработать событие отправки формы, нужно извлечь зна­
чения из обоих фрагментов при помощи такого кода: WinJS.UI.processAl l ( ) .done ( func t i on ( )
var frmOrder = document.getEl ementByid ("frmOrder");
// Сформировать заказ frmOrder.addEventLi s t ener ("submit", funct ion ( е ) e.preventDefaul t ( ); var order = { Ьi l l ing_street: document.querySelector ("#divBi l l ingAddres s. 4inpStreet") . value, Ьi l l ing_c i ty: document.querySelector ("#divBi l l ingAddres s. 4inpCi ty") .value, shipping_s treet: document.querySelector ("#divShippingAddress. 4inpStreet") . value, shipping_ci ty: document.querySelector ("#divShippingAddress. 4inpCity") . value } ; } ) ; /! Сохранить в базе данных } ) ; Глава 1 О. Фрагменты страниц и навигация Здесь метод querySelector ( ) применяется для того, чтобы из­
влечь значения обоих адресов: для выставления счетов и доставки. Затем эти значения записываются в объект order.
Предупреждение. Незабывайте вызывать методwinJS. u r .proces sAll ( ), иначе HtmlControl так никогда и не станет элементом управления. Примечание. В приложении Магазина Wi ndows нельзя использовать сер­
верную директиву #INCLUDE для включения содержимого другого файла. Для этой цели предназначен элемент управления HtmlCon trol. Создание страничного элемент а управления Страничный элемент управления позволяет без особого труда создать элемент управления WinJS из НТМL-страницы, JavaScript-фaйлa и СSS-файла. Создавать страничный элемент имеет смысл, когда нужно не прос­
то добавить на страницу фрагмент НТМL-кода, а инкапсулировать внешний вид и поведение. Ко всему прочему, у страничного элемента управления имеется собственный жизненный цикл, определяемый событиями. Так, у него есть событие ready, в обработчике которого можно произвести ини­
циализацию элемента. Пусть, например, требуется создать элемент управления Aleгt. Если пользователь нажмет кнопку, отрисовываемую этим элементом, то должен появиться модальный диалог WinRT, содержащий сооб­
щение (рис. 1 0.2). Далее я подробно опишу все шаги создания такого страничного элемента управления. Рис. 1 0.2. Показ оповещения с помощью страничного элемента управления Создание ст раничного элемента управления
Прежде всего, мы должны создать три файла: alert.html • aleгt.js aleгt.css Для этого добавьте в проект файлы типа :НТМL Page, JavaScгipt File и Style. В файл aleгt.html мы поместим всю разметку нового элемента уп­
равления: <!DOCTYPE html> <html> <head> <meta charset = • ut f - 8" /> <t i t l e>conf i rmButton</t i t le> < l ink href ="alert.c s s • rel ="stylesheet • /> <s cript src ="alert.j s"></s cript> </head> <body> <butt on c l as s ="alert"></butt on> </body> </html> Обратите внимание, что файл aleгt.!1tml представляет собой обыч­
ную :НТМL-страницу, содержащую секции :НЕАD и BODY. В секции :НЕАD находятся ссылки на таблицы стилей и JаvаSсгiрt-файлы, не­
обходимые странице, а в секции BODY - разметка элемента управле­
ния Aleгt. Примечание. Все скрипты и таблицы стилей, упоминаемые в заголовке НТМL-файла страничного элемента управления, переносятся из этого фай­
ла в объемлющую страницу. Файл alei-t.css выглядит так (не слишком впечатляюще): .alert { background- color: red Этот СSS-файл изменяет цвет фона кнопки Aleгt на красный. И наконец, приведу файл alei-t.js: /// <reference path="//Micros of t.WinJS.1.0/j s/base.j s • /> /// <reference path="//Microsof t.WinJS.1.0/j s/ui.j s • /> ( func t i on ( ) { "use stri ct"; var Alert = WinJS.UI.Pages.de f ine ("myControl s/alert.html", ready: funct i on ( el ement, opt i ons ) { m1••11J1 Глава 1 О. Фрагменты страниц и навигация } } ) ; var btn = WinJS.Ut i l i t i es.query ("button", element ); 1 1 Задать значения параметров по умолчанию opt i ons .buttonLabel = opt ions .buttonLabel 1 1 "Show Al ert" opt i ons.mes s age = opt ions.mes s age 1 1 "Alert!!!"; 1 1 Изменить текст на кнопке btn [ O ] .innerText = opt i ons.but t onLabel; 1 1 Настроить обработчик события щелчка по кнопке btn. l i s ten ( "c l ick", funct ion ( ) { var md = new Windows.UI.Popups.Mes s ageDi alog ( opt ions.mes s age ) ; md. showAsync ( ) ; } ) ; WinJS.Namespace.de f ine ("MyControl s", Alert: Alert } ) ; } ) ( ); В JavaScгipt-фaйлe aleгt.j s создается элемент управления Aleгt. Для этого вызывается метод WinJS. Pages. define ( ),которому пере­
дается путь к файлу aleгt.html. При создании страничного элемента управления можно опреде­
лить обработчик события ready. Это событие генерируется после отрисовки элемента, так что в его обработчике можно без опаски об­
ращаться к любому элементу в файле aleгt.html. В показанном выше коде обработчик события ready настраивает обработчик события щелчка по кнопке. В ответ на нажатие кнопки должен быть показан диалог Windows. UI. Popups .Mes s ageDialog. В файле aleгt.j s есть еще одна важная часть. Благодаря следую­
щему коду элемент управления Aleгt помещается в глобальное про­
странство имен, после чего его можно декларативно использовать в других НТМL-страницах: WinJS.Namespace.de f ine ("MyControl s", Alert: Alert } ) ; После того как элемент управления Aleгt создан, его можно по­
местить на страницу. На следующей странице отображается кнопка с надписью Cl ick Неге! При нажатии на нее появляется оповещение You Cl icked tЬе Butto11! Создание многостраничных приложений <!DOCTYPE html > <html> <head> <meta charset ="ut f - 8" /> <t i t l e>Fragment s </t i t l e> <!- - Ссылки на WinJS - - > "11• ••ЕШ1 <l ink hre f="//Microso f t.WinJS.1.0/c s s/ui -dark.cs s • '+-rel ="s tyl esheet • /> <s cript src="//Microso f t.WinJS.1.0/j s/base.j s"></script> <script src="//Microso f t.WinJS.1.0/j s/ui.j s"></script> <script type ="t ext/j avascript" src ="pageControl.j s"></script> <script type ="t ext/j avascript" src ="/pageControl/alert.j s"> '+-</script> </head> <body> <div id="btnDel ete" data-win- contro l ="MyControl s.Alert • data-win-opt i ons ="{ but tonLabel: 'Cl i ck Here!', mes s age: 'You c l i cked the but t on!' }"></div> </body> </html > Отметим, что эта страница ссылается на JavaScгipt-фaйл aleгt.js. Чтобы на странице можно было декларативно использовать создан­
ный вами страничный элемент управления, ссылка на этот файл обя­
зательна. Страничный элемент управления Aleгt .п:е кларатив но ис польз у­
ется в теле страницы точно так же, как любой стандартный элемент управления WinJS, например Lis tView или DatePicker. Предупреждение. Незабывайте вызывать методwinJS. UI .pr oces sAl l ( ), иначе написанные вами страничные элементы никогда не превратятся в эле­
менты управления - как и любые другие элементы управления Wi nJS. Создание многостраничных приложе ний Приложение Магазина Windows может состоять из нескольких НТМL-страниц, связанных ссылками. Однако Майкрософт настоя­
тельно не рекомендует так поступать, а предлагает создавать одност-
Глава 1 О. Фрагменты страниц и навигация раничное приложение и динамически загружать страничные элемен­
ты управления, представляющие отдельные страницы. Чем объяснить такую рекомендацию? Приложение Магазина Windows - это не веб-сайт (хотя при его создании применяются мно­
гие веб-технологии), а приложение Windows. Пользователи ожидают различного поведения от приложения Windows и от веб-приложения. В веб-приложении считается нор­
мальным, когда после щелчка по ссылке приходится ждать (и ждать, и ждать ... ), пока загрузится другая страница. Пока пользователь ждет, приложение не отвечает ни на какие его действия. После загрузки но­
вой страницы появляется совершенно другой контекст. В приложении Windows та�<ай способ работы неприемлем. Напри­
мер, Micгosoft Woгd никогда не «застывает» после нажатия кнопки (ну почти никогда). В приложении Windows отсутствует навигация между страницами. Навигация сводится к открытию диалоговых окон и переходу между вкладками. Чтобы реализовать аналогичную схему в приложении Магазина Windows рекомендуется делать приложение одностраничным. Создание приложения с навигацией Простейший способ создать одностраничное приложение, содержа­
щее несколько страниц, - воспользоваться шаблоном проекта Visual Studio Navigation Арр ( Приложение навигации) (рис. 1 0.3). Между этим шаблоном и шаблоном Blank Арр (Пустое приложение) есть не­
сколько отличий. "'lnШtled "Twiplates • JavaScript W'ind� Sto� tightSwitch t, Oth�languagu � 0thf1' Projtct Typts ModtlingProjtcts N•m� Na,•App 1 NewProject .NЕТ F-ramtwork4.5 • Sart Ьу. Ddiult �] БlankApp JavaScript r-Jt' ;::.J Gridдpp J11vaScfipt 1"'
"' :;J SplitApp 1.w•Sc::ript !:J F11.� L11yout Арр J11vaScript ь;;J Nsvi9a1ionдpp JavaSaipt lo,�tian: C:\U �t�\Sttphefl\Doc.uments\Visual Studio 2012\ProjЮ�\ Solutionпamf.: f<J,н4ipp1 �j� [�g! Sl!,жh !1•�t�ll<'d r,.. Р · Туре: J1v•Sc1ipt А prajtct for а 'flindows Stort арр th1t has p1Юd"1ntd(cntra!sfi:irnavi!.Jfticn, � (;а [reatt dir.-dory lor �otution Oдddtosourcl! contra! Рис. 1 0.З. Создание приложения с навигацией Создание многостраничных приложений lll••ED Во-первых, в шаблоне Navigation Арр имеется папкараgеs, в кото­
рую помещаются все страничные элементы управления, представля­
ющие страницы приложения. Каждый страничный элемент помеща­
ется в отдельную подпапку этой папки. Шаблон Navigation Арр включает папку pages/home, где находит­
ся элемент, представляющий начальную страницу. Он загружается по умолчанию при запуске приложения Магазина Windows. Шаблон Navigation Арр, как и шаблон Blank Арр, включает файл default.html. Но этот файл является всего лишь оболочкой для загруз­
ки различных страничных элементов управления из папки pages. При переходе от одной страницы к другой тело файла default.html заменя­
ется содержимым новой страницы. Наконец, шаблон Navigation Арр включает элемент управления WinJS PageControlNavigator, отсутствующий в шаблоне Blank Арр. Этот элемент находится в файле j s/navigatoг.js и отвечает за все детали загрузки страничных элементов управления на страницу default.html. Давайте глубже разберемся в том, как все это работает. Страница default.html приложения с навигацией Как я уже отмечал, страница default.html в приложении с навигацией в основном играет роль оболочки для содержимого страничных эле­
ментов управления. Весь ее код приведен в листинге 1 0.1. Ли сти нг 1 0.1. Страница default.html <!DOCTYPE html> <html> <head> <met a charset ="ut f - 8" /> < t i t le>Mul t i Page</t i t l e> <!- - Ссылки на WinJS - - > < l ink hre f ="//Micros of t.WinJS.1.0/c s s/ui -dark.cs s • �re l ="s tylesheet • /> <script src="//Microso f t.WinJS.1.0/j s/base.j s"></script> <script src="//Micros of t.WinJS.1.0/j s/ui.j s"></script> <!- - Ссылки на Mult i Page - - > < l ink hre f ="/c s s/def aul t.c s s • rel ="s tyl esheet • /> <script src= •/j s/def ault.j s"></script> <script src= •/j s/navigator.j s"></script> </head> ED•••I Глава 1 О. Фрагменты страниц и навигация <body> <div i d="cont enthos t" data-win-control ="Appl icati on. 4PageControlNavigator"data-win- opt ions="{ home: '/page s/home/ 4home. html • } "></div> <!- - <div i d="appbar" data-win-contro l ="WinJS.UI.AppBar"> <butt on data-win- control ="WinJS.UI.AppBarCommand" 4data-win-opt i ons ="{ id: 'cmd', l abel: 'Command', i con: 'placehol der'}"></but t on> </div> - - > </body> </html > В теле этой страницы находятся два элемента управления. Пер­
вый - PageControlNavigator; он имеет идентификатор contenthos t и служит контейнером для содержимого, загружаемого из странич­
ных элементов управления. Подробнее мы поговорим об элементе PageControlNavigator чуть ниже. Второй (закомментированный) элемент - AppBar. Все, что нахо­
дится в файле default.html, отображается на каждой странице прило­
жения. Поскольку панель приложения (АррВаг) должна быть оди­
накова для всех страниц приложения Магазина Wirнlows, то default. html - самое подходящее место для нее. Примечание. Элемент управления AppBar рассматривался в главе 6. Все прочее содержимое, помещенное в файл default.html, также отображается на всех страницах приложения с навигацией. Напри­
мер, если вы захотите создать стандартный верхний или нижний ко­
лонтитул либо боковую панель, имеет смысл включить эти элементы в файл deЬшlt.11tml. Добавление страничных элементов управления в приложение с навигацией Ради одностраничного приложения с единственным страничным эле­
ментом управления - начальной страницей - не стоило и стараться. Поэтому добавим вторую страницу. Проще всего добавить в проект страничный элемент, воспользо­
вавшись шаблоном Page Contгol, имеющимся в Visual Studio. Соз­
дайте подпапку anotheгPage в папке pages. Выберите из меню пункт Pгoj ect -) Add New Item ( Проект -) Добавить новый элемент) и затем шаблон Page Contгol ( Эле.мент управления страницей') (рис. 1 0.4). Назовите новый страничный элемент aнotl1eгPage.l1tml. 1 и� 1с1ню т:ш 11c peпc дt:l l U IJ JIUl(a JI ИЗOП<tl l l lOЙ uерсии Vist1 al SLL1 di u. llµiш. иерее. Создание многостраничных приложений .,; Ja11aScript Vlindo\.� Store Code Dat.J l· Onlinc: №m� p119кonlrol.hlml Add New ltem - NavApp1 Sortby: Dtf11ult Elr.,j Filc Optn Picker Contr11ct J1vaScript О. Р19е Control J;мsXript .Р� Se11rch Contfi!ct t:JГ:j Share T11rget Conlr11ct J11v5Script J111111Script Туре: J11v11Script д Ь111nk: pagl! control that includ6 HTML, J S;snd CSSfile�. Рис. 1 0.4. Создание нового страничного элемента в Visual Studi o В результате добавления элемента anotl1eгPage.html в проект со-
здаются следующие три файла: anotheгPage.html; anotheгPage.js; anotheг Page.css. Разумеется, их можно было бы создать и вручную, но зачем, если можно попросить Visual Studio? Содержимое файла anotheгPage.html приведено в листинге 1 0.2. Обратите внимание на тег HTMLS HEADER в теле страницы (не пу­
тать с тегом HEAD). Внутри него находится кнопка << Назад:» и назва­
ние страницы (рис. 1 0.5). Рис. 1 0.5. Верхний колонтитул страничного элемента управления Ли сти нг 1 0.2. Содержимое файла anotherPage.html <!DOCTYPE html> <html> <head> <meta charset="ut f - 8" /> Глава 1 О. Фрагменты страниц и навигация <t i t l e>anotherPage</t i t le> <!- - WinJS references - - > < l ink hre f="//Microsof t.WinJS.1.0/c s s/ui-dark.cs s" 4 re l ="stylesheet" /> <script src="//Micros of t.WinJS.1.0/j s/base.j s"></script> <s cript src="//Microsof t.WinJS.1.0/j s/ui.j s"></script> < l ink hre f ="anotherPage.cs s" rel ="s tyl esheet" /> <script src ="anotherPage.j s"></script> </head> <body> <div c l as s ="anotherPage f ragment"> <header ari a- l abe l ="Header content" rol e ="banner"> <butt on cl ass="win-backbutton" ari a- l abe l ="Back" di saЫed> 4</but t on> <hl cl as s ="t i t l earea win- type- e l l ips i s"> < span cl as s ="paget i t l e">Wel come t o anotherPage</span> </hl> </header> <sect i on ari a-l abel ="Main cont ent" role="main"> <p>Content goes here.</p> </sec t i on> </div> </body> </html > Чтобы было видно, что мы действительно перешли на страницу anotheгPage.html, я заменю текст <iConteпt goes 11еге» на «Hello fгom Anotheг Page!». Содержимое файла aпotheгPage.js приведено в листинге 1 0.3. Листинг 1 0.3. Содержимое файла anotherPage.js ( funct i on ( )
"use stri ct";
WinJS.UI.Pages.de f ine ("/page s/anotherPage/anotherPage.html", // Эта функция вызывается при любом переходе на эту страницу. //Она заполняет находящиеся на странице элементы данными. ready: funct i on ( el ement, opt i ons ) { // TODO: здесь страница инициализируется. } ' updateLayout: funct i on ( el ement, viewState, las tVi ewState ) /// <param name ="e l ement" domEl ement ="true" /> /// <param name="viewState" value="Windows.UI.ViewManagement. 4Appl i cati onVi ewStat e" /> /// <param name="lastViewState" value="Windows.UI.ViewManagement. 4Appl i cat i onVi ewState" /> // TODO: обработка изменений состояния просмотра. Создание многостраничных приложений l l B ••ВD } ' unload: funct i on ( ) { 1 1 TODO: обработка ухода с этой страницы } } ) ; } ) ( ); Обратите внимание, что в этом коде определяется новый стра­
ничный элемент управления. У него есть три обработчика событий: ready, upda teLayout и unload. В следующем разделе я изменю обра­
ботчик события ready, поддержав страничную навигацию. Переход на другую страницу Теперь в моем приложении есть две страницы: начальная и anotherPage. Спрашивается: как перемещаться между ними? Для этого предназначен метод WinJS. Naviga te. naviga te ( ) . Давайте изменим начальную страницу, так чтобы можно было пе­
рейти с нее на страницу anotherPage. Добавьте в главный раздел на­
чальной страницы гиперссылку: <sect i on aria- l abe l ="Main cont ent • role= •main"> <p>Content goes here.</p> <а i d ="lnkAnotherPage">Vi s i t Another Page</a> </sec t i on> У этой гиперссылки есть атрибут ID, но нет атрибута HREF. Примечание. Рассматриваемый в этом разделе код можно найти в папке Chapter 1 0\Mu l ti page проекта на сайте Gi tHub. Затем я модифицирую код в файле home.js, настроив обработку события щелчка по гиперссылке в функции ready: WinJS.UI.Pages.de f ine ("/page s/home/home.html •, { ready: funct i on ( el ement, opt i ons ) { var lnkAnotherPage = document.getElementByid ("lnkAnotherPage"); lnkAnotherPage. addEventLi s tener ( "c l i ck•, funct ion ( evt ) { evt.preventDefaul t ( ); WinJS.Navigation.navigate ("/pages/anotherPage/anotherPage.html"); } ) ; } } ) ; Обработчик события click сначала вызывает метод preventDefaul t ( ),чтобы предотвратить обычный переход по ссыл­
ке. Напомню, что здесь мы имитируем навигацию. Глава 1 О. Фрагменты страниц и навигация Затем для перехода на страницу anotherPage. html вызывается метод WinJS. Navigation. navigate ( ). Теперь, щелкнув по ссылке на начальной странице, вы попадете на страницу anotherPage (рис. 1 0.6). И более того - кнопка «Назад» на странице anotherPage тоже работает. Щелкнув по ней, вы вернетесь на начальную страницу. Рис. 1 0.6. Переход между страницами Структура АР/ навигации Метод WinJS. Navigatation. navigate ( ) - лишь один из методов API навигации в WinJS. Перечислим остальные. Ьасk ( ) - возврат на предыдущую страницу. Если передать це­
лочисленный параметр, то можно вернуться на указанное чис­
ло страниц. forward ( ) - переход на следующую страницу в журнале исто­
рии. Если передать целочисленный параметр, то можно перей­
ти вперед на указанное число страниц. naviga te () - переход на конкретную страницу ( страничный элемент управления). Можно также передать объект, пред­
ставляющий начальное состояние страницы. В API навигации определены также следующие свойства. canGoBack - возвращает true, если возврат назад возможен. canGoForward - возвращает true, если переход вперед возмо­
жен. history - возвращает объект, представляющий историю пе­
реходов. У этого объекта есть свойства current, backStack и f or war dS t at e. location - возвращает URL текущей страницы. state - возвращает состояние текущей страницы. Наконец, API поддерживает три события: Создание многостраничных приложений beforenavigate; naviga ting; navigated. l l l ••EШ У каждого из них имеется свойство detail, из которого можно по­
лучить свойства location и s tate. Элемент управления PageContro/Navigator Элемент управления PageControlNavigator включен в шаблоны проектов Visual Studio Navi gation Арр, Gгid Арр и Split Арр, но не включен в шаблоны Blank Арр и Fixed Layout Арр. У него две основ­
ные функции. Во-первых, элемент PageControlNavigator обрабатывает со­
бытие naviga ted и отвечает за загрузку нужного страничного эле­
мента управления в ответ на это событие. Например, если вызвать метод WinJS.Navigation.navigate ("/page s/s omePage/s omePage. html"), то PageControlNavigator загрузит страничный элемент somePage.html на страницу default.html. В-вторых, этот элемент активирует или деактивирует кнопку <�:На­
зад». Если свойство WinJS. Navigation. canGoBack равно true, то кнопка <�:Назад» активна, иначе - нет. Состояние навигации При создании многостраничного приложения с применением API навигации можно воспользоваться состоянием навигации для сохра­
нения состояния при перемещениях между страницами. В каждой записи журнала истории имеется свойство s ta te, в которое можно записать произвольное значение. Пусть, например, требуется разработать простой каталог товаров. Приложение содержит всего две страницы: начальную, на которой отображается список категорий, и детальную - для показа списка то­
варов в выбранной категории (см. рисунки 1 0.7 и 1 0.8). После щелчка по категории на начальной странице мы видим список входящих в нее товаров на детальной странице. Если после выбора категории на начальной странице мы перешли на детальную страницу, а затем вернулись назад, то желательно, что­
бы была выбрана та же самая категория. Тогда посетитель будет знать, что он только что просматривал и не потеряется в списке категорий (рис. 1 0.9). Глава 1 О. Фрагменты страниц и навигация Рис. 1 О.7. Начальная страница приложения Рис. 1 0.8. Детал ьная страница приложения Рис. 1 0.9. Сохранение состояния приложения Примечание. Код, рассматриваемый в этом разделе, можно найти в nanкe Chapter1 0\Navi gati onState проекта на сайте Gi t Hub. Создадим такое приложение. На начальной странице поместим элемент Li s tView, в котором будем отображать список категорий то­
варов: Создание многостраничных приложений <div i d="tmplCategory" data-win-control ="WinJS.Binding.Templat e"> <div c l as s ="cat egoryi t em"> l l l ••ВD <span data-win-bind="t extCont ent:categoryName"></span> </div> </div> <div i d="lvCategori es" data-win-contro l ="WinJS.UI.Li stView" data-win-opt i ons ="{ i temTemplate: sel ect ('#tmpl Category'),
s el ecti onMode: 's ingl e', t apBehavior: 'directSelect' , swipBehavi or: 'sel ect' } "> </div> Элемент Lis tView инициализируется в обработчике события ready страничного элемента управления: WinJS.UI.Pages.de f ine ("/pages/home/home.html", ready: funct i on ( el ement, opt i ons ) { var lvCategories = document.getElementByi d ("lvCategori es"). '+winControl; // Привязываем категории к Li s tview var dsCategori es = new WinJS.Binding.Li s t ( MyApp. '+categori esAndProduct s ); lvCategori es.it emDat aSource = dsCategori es.dataSource; //Читаем индекс выбранной категории из свойства s t ate WinJS.Navigat i on.state = WinJS.Navigat i on.s t ate 1 1 { }; var sel ectedCategoryindex = WinJS.Navigat i on.state.sel ectedCat egoryindex; i f ( s electedCat egoryindex > - 1 ) { lvCategori es.s el ect i on.s et ( s el ectedCat egoryindex); // Если какой-нибудь элемент выбран, переходим к нему lvCategories.addEventListener ("selectionchanged", function ( )
i f ( lvCategories.sel ect i on.count ( ) > 0 ) { lvCategori es.s el ect i on.get i t ems ( ) .done ( funct i on ( it ems ) //Сохраняем индекс выбранной категории в истории var s el ectedCategoryindex = i tems [ O J .index; WinJS.Navigat ion.state = { selectedCategoryindex: s el ect edCat egoryindex } ; // Переход к категории с выбранным названием var sel ectedCategoryName = i tems [ O J .data.cat egoryName; } } ) ; } } ) ; Глава 1 О. Фрагменты страниц и навигация WinJS.Navigat i on.navigat e ( "/pages/detai l s/detai l s.html", { s el ect edCategoryName: sel ectedCat egoryName ) ; } ) ; В показанном выше коде происходит несколько вещей. В первых двух строчках обработчика события ready мы привязываем элемент управления Li s tView к списку categoriesAndProducts, содержаще­
му сами данные. Примечание. Отметим, что при работе со страничными элементами уп­
равления вызывать метод WinJS. UI. proces sAll ( ) для инициализации Li s tView необязательно. Об этом позаботится сам страничный элемент. Далее, если в состоянии навигации был сохранен индекс выбранной категории, то мы читаем его и устанавливаем в элемент Li s tView.
Следующая часть кода - прослушиватель события s election­
changed элемента Li s tView. Если выбрана новая категория, то ее индекс необходимо сохранить в состоянии навигации. Обработчик события s electionchanged также отвечает за переход к детальной странице. Вызывая метод WinJS. Navigation. navigate ( ), он передает детальной странице название выбранной категории. На детальной странице также имеется элемент Li s tView для по­
каза списка товаров в выбранной категории. Обработчик события ready на этой странице отвечает за привязку Li s tView к списку то­
варов: WinJS.UI.Pages.de f ine ("/pages/detai l s/detai l s.html", ready: funct i on ( el ement, opt i ons ) { var lvProduct s = document.getElementByid ("lvProduct s").
'+winControl; 1 1 Получаем выбранную категорию var s el ectedCategoryName = opt i ons.select edCategoryName; !! Фильтруем товары по категории var selectedCategory = MyApp.categoriesAndProduct s.f i lter '+( funct i on ( cat egory ) { return cat egory.cat egoryName == s electedCategoryName; } ) ; /! Привязываем Li s tView к списку товаров Резюме •••••ШJ var ds Product s = new WinJS.Binding.Li s t ( s el ec tedCategory [ O ]. '+products 1; } } ) ; lvProduc t s.i temDataSource = ds Produc t s.dataSource; Название выбранной категории мы читаем из объекта options, пе­
реданного обработчику события ready. Затем список товаров, прина­
длежащих этой категории, записывается в свойство i temDa tasource элемента Li s tView. Р ез юме В этой главе мы уделили внимание двум вопросам: использование общего фрагмента НТМL-кода на нескольких страницах и построе­
ние приложений с несколькими страницами. В первой части главы я рассмотрел элемент управления HtmlControl, позволяющий вклю­
чать одну НТМL-страницу в другую. Затем мы поговорили о страничных элементах управления, кото­
рые позволяют легко создавать новые элементы управления WinJS из НТМL-страницы,JаvаSсгiрt-файла и таблицы стилей. Наконец, я объяснил, как создать одностраничное приложение, содержащее несколько страниц. Вы научились использовать элемент управления, который в сочетании с API навигации позволяет дина­
мически загружать страничные элементы, создавая иллюзию навига­
ции по отдельным страницам. rЯАВА 1 1. Ра бо та с Live Connect API В этой главе:
• Установка Live SDК.
• Аутентификация пользователя.
• Передача маркера аутентификации веб-службе.
• Получение основной пользовательской информа­
ции. • Скачивание и закачивание файлов в SkyDгive.
Интерфейс Live Connect API предназначен для подключения к служ­
бам Live из приложения Магазина Windows. Службы Live позволяют пользователю аутентифицироваться, получить свою информацию (в том числе календари и списки контактов) и взаимодействовать со своим облачным хранилищем SkyDг ive. В первой части этой главы я объясню, как установить и настроить комплект Live SDK. В частности, вы узнаете, как зарегистрировать свое приложение Магазина Windows на сайте Live Connect. Затем мы рассмотрим функцию единого входа без ввода данных (zeI"o-click single sign-on). Она позволяет пользователю приложения Магазина Windows аутентифицироваться, не вводя своего имени и пароля. Я также объясню, как воспользоваться преимуществами аутен­
тификации в случае, когда приложение Магазина Windows взаимо­
действует с удаленной веб-службой. Я покажу, как передать маркер аутентификации из приложения веб-службе. Вы узнаете также, как получать от служб Live пользовательскую информацию, например имя и фамилию, день рождения и фотогра­
фию. Установка Live SDK
Наконец, мы обсудим облачное хранилище SkyDгive. Вы научи­
тесь закачивать туда файлы из приложения Магазина Windows и ска­
чивать их обратно. Установка Live SDK Прежде чем приступать к использованию Live Connect API, необхо­
димо скачать последнюю версию комплекта Live SDK для Windows и Windows Phone с сайта Майкрософт. Он находится в центре раз­
работки приложений Магазина Windows по адресу http://msdn. micmsoft.com/ en -us /windows/ apps/ Добавление ссылки на Live SDK Скачав Live SDК, необходимо добавить ссылку на библиотеку Live SDK JavaScгipt в свой проект приложения Магазина Windows в Visual Studio. Выберите из меню команду Pmj ect � Add Refeгence ( Проект � Добавить ссылку). Затем щелкните по ссылке Windows � Extensions ( Расширения) на левой панели и отметьте флажок в стро­
ке Live SDK (рис. 1 1.1 ) Предупреждения. После скачивания Live SDK необходимо перезапустить
Vi sual Studi o, только тогда Live SDK появится в диспетчере ссылок. tlUl'i �f& f$tft1 M.:•:1-:Ji:','�<:;o;(_,, f_..N;,..,t!!'нr:З1' :'!/ >,';,_",k>,.;"(;j:,,�Jif,,..J .•• "\«:f?l !;;.1':: Р · r. -���.�.:· . ..J С ...... �.-....... ; l.-.... �.!.�.� ..... J Рис. 1 1. 1. Добавление ссылки на библиотеку Live SDK Ja vaScript Затем раскройте папку Refeгences � Live SDK � JS и перетащите файл wl.j s file на страницу default.html. В результате будет добавлена такая ссылка наjаvаSсгiрt-файл: <script type="text/j avascript" src="/LiveSDKHTML/j s/wl.j s"></script> Глава 1 1. Работа с Live Connect API Регистрация приложения Перед тем как использовать Live Connect API, нужно зарегистрировать свое приложение на сайте Live Connect. Перейдите по ссылке https:// manage.dev.live.com/build. Пусть вас не смущает название сайта - Windows Pusl1 Notifications & Live Connect; вы попали как раз туда, куда нужно. Для регистрации своего приложения следуйте инструкциям. Обратите внимание на шаг 3, где требуется изменить информацию в манифесте приложения. После регистрации на экране появляется ваш «секрет клиента» (client secret) (рис. 1 1.2). Запишите его, потому что он понадобится позже (при работе с веб-службой). Рис. 1 1.2. Получение секрета клиента Примечание. Надеюсь, вы понимаете, что секрет клиента нужно хранить в секрете. Тот секрет, который приведен в книге, я позже сгенерировал зано­
во. Требуется настроить еще одну вещь. Зайдите на страницу https:// manage.dev.live.com/ Applications/Iвdex и введите свой домен перена­
правления (Redirect Domai 11). Щелкните по имени приложения, затем перейдите на вкладку API Settings (Настройки API) и заполните поле Rediгect Domai11. Мо­
жете ввести любое доменное имя, лишь бы оно не было уже занято (рис. 1 1.3). с�н!f' «a>)X(\Hbl f1 U Рис. 1 1 .3. Создание домена перенаправления Ус тановка Live SDK Для этой главы я ввел доменное имя http:j /livesdkdemo.su perexpert. com/ Предупреждение. Домен перенаправление - это доменное имя, а не URL­
aдpec. Если у вас нет никаких идей по поводу домена, вспомните, что домен не обязан указывать на что-то реально существующее. Примечание. Если вы уже отправляли свое приложение в Магазин Wi ndows, то процедура регистрации на сайте Live Connect будет иной. Зайдите на инструментальную панель ( Dashboard), измените параметры приложения и перейдите по ссылке Advanced Features (Дополнительные возможности). Следуйте инструкциям по настройки служб Live Connect. Инициализация Live Connect SDK Прежде чем обращаться к любой службе с помощью Live Connect API, необходимо инициализировать подключение. Для этого вызывается метод WL. ini t ( ) , как показано в листинге 1 1.1. Ли сти нг 1 1. 1. Инициализация подключения
var REDIRECT_DOMAIN = "ht tp://l iveSDKDemo.Superexpert.com"; var s copes = [ "wl.s ignin" ];
WL. init ( { scope: scopes, redirect_uri: REDIRECT_DOMAIN } ) ; WL. login ( ) . then ( func t i on ( l oginResul t s ) //Получилось! Теперь можно что-то депать ...
} ' funct i on ( loginRespons e ) { } ) ; throw WinJS.ErrorFromName ("Fai l ed t o login!"); Метод WL.init ( ), показанный в листинге 1 1.1, имеет два аргу­
мента: redirect_uri и массив контекстов. Аргумент redirect_uri
должен в точности совпадать с доменом перенаправления, заданным, как описано в предыдущем разделе. А массив контекстов мы обсудим в следующем разделе. Задание различных контекстов Массив контекстов, передаваемый методу WL. ini t ( ), определяет, к какой информации у вас есть доступ. Из учетной записи пользова-
1m•••• Глава 1 1. Работа с Live Connect АР/ теля в Windows Live можно извлечь разнообразные сведения, в том числе дату рождения, адреса электронной почты, почтовый адрес, фо­
тографии и контакты. Ниже приведен неполный список контекстов. wl. signon - поведение единой точки входа. wl. Ьasic - доступ для чтения основной информации из про­
филя и списка контактов. wl. offline access - возможность читать и обновлять пользо­
вательскую информацию, даже когда пользователь не работа­
ет с вашим приложением. wl. Ьirthday - доступ для чтения даты рождения. wl. calendars - доступ для чтения календарей и мероприя­
тий. wl. calendars_update - доступ для чтения и записи календа­
рей и мероприятий. wl. contacts_Ьirthday - доступ для чтения даты рождения самого пользователя и лиц в списке его контактов. wl. contacts_create - доступ к списку контактов с правом записи. wl. contacts_calendars - доступ для чтения календарей и мероприятий самого пользователя и лиц в списке его контак­
тов. wl. contacts_photos - доступ к мультимедийным матери­
алам самого пользователя и предоставленным в общее с ним пользование другими пользователями. wl. con tacts _ skydri ve - доступ для чтения к файлам пользо­
вателя в Sky Dгive и к файлам, предоставленным в общее с ним пользование другими пользователями. wl. emails - доступ для чтения адресов электронной почты пользователя. wl. events create - доступ для создания новых мероприя­
тий. wl. messenger - разрешение входить от имени пользователя в службу MSN Messeпgeг. wl. phone _ numЬers - доступ для чтения номеров телефонов пользователя. wl .photos - доступ для чтения мультимедийных материалов пользователя. wl .postal_addresses - доступ для чтения почтовых адресов пользователя. Установка Live SDK
l lll ••EШ wl. share - разрешение изменять сообщение о состоянии пользователя. wl. skydrive - доступ для чтения файлов пользователя в SkyDrive. wl. skydrive_update - доступ для чтения и записи файлов
пользователя в SkyDгive. wl. work _profile - доступ для чтения к информации о работе и послужном списке пользователя. Примечание. Полный перечень контекстов см. на странице http://msdn.
mi crosoft.com/en- us/l i brary/l ive/hh243646.aspx. Очевидно, не все пользователи приложения Магазина Windows хотят раскрывать информацию о себе. Поэтому ваше приложение должно получить согласие пользователя на доступ к этой информа­
ции. При первом запуске приложение Магазина Windows, которому нужен некоторый контекст, выводит модальный диалог, где спраши­
вает у пользователя разрешение. Так, на рис. 1 1.4 показан диалог, ко­
торый появляется, когда метод WL. ini t ( ) вызывается с контекстом wl. s i gnon. Рис. 1 1.4. Приложение Магазина Wi ndows спрашивает разрешение осуществл ять автоматический вход от вашего имени Дав приложению разрешение на доступ к пользовательской ин­
формации в Windows, вы впоследствии сможете его отозвать. Для EШ•• l l l Глава 1 1. Работа с Live Connect API этого нужно войти в свою учетную запись на сайте Live.com, выбрать приложение и отозвать разрешения (рис. 1 1.5). !.� ·;-.. .> -�,'�' "<:;. �\�:.�,,;�/�-�,�·�У.У-"::;;_;.,·;;::;.;.. Рис. 1 1.5. Отзыв разрешений, выданных приложению Магазина Wi ndows Аутентификация польз ов ат еля Самая полезная служба Live Connect - аутентификация. Live Connect поддерживает так называемый единый вход без ввода данных ( zem­
click single sign-on). Если вы входите на свой компьютер с ОС Windows, пользуясь собственной учетной записью в Windows Live, или если с вашей учет­
ной записью в ОС Wiнdows ассоциирована учетная запись в Windows Live, то входить еще раз в Windows Live при работе с приложением Магазина Wiнdows не нужно. Вам не придется вводить ни имя поль­
зователя, ни пароль. Примечание. При первом запуске приложения Магазина Wi ndows поль­
зователь должен дать ему разрешение на автоматический вход ( рис. 1 1.4). Но вводить учетные данные при этом не требуется. Использование метода WL.login() Существует два способа войти в службу Live Conнect: программный и декларативный. Можно воспользоваться методом WL. login ( ) или Аутентификация пользователя элементом управления WL. ur. S i gnOn. Если вы хотите аутентифици­
ровать пользователя сразу после запуска приложения, то необходимо использовать метод WL. login ( ). В листинге 1 1.2 приведен пример входа от имени текущего поль­
зователя. Если все пройдет нормально, то на странице появится сооб­
щение «Connectedi>. Листинг 1 1.2. Использование метода WL.login() ( func t i on ( ) { н use stri ct";
var REDIRECT_DOMAIN = "http://l iveSDKDemo.Superexpert.com"; funct i on init ( ) { var REDIRECT_DOMAIN = "http://l iveSDKDemo.Superexpert.com"; var spanResul t s = document.getEl ementByid ("spanResul t s"); var scopes = ["wl.s ignin"];
WL.init ( { s cope: scopes, redirect_uri: REDIRECT_DOMAIN } ) ; WL. login ( ) . then ( func t i on ( loginResul t s ) spanResul t s.innerText } ' funct i on ( loginRespons e ) spanResul t s.innerText ) ; "Connected"; "Error when call ing WL.login"; document.addEventLi s tener ("DOMContentLoaded", init ); } ) ( ); Использование элемента управления Signln Не во всех приложениях Магазина Windo>vs обязательно аутентифи­
цироваться в самом начале работы. Аутентификация может быть фа­
культативной. Но у аутентифицированного пользователя могут быть дополнительные возможности (например, приложение показывает его имя). Втакомслучаеимеетсмыслвоспользоватьсяэлементомуправления S ignin. Его можно поместить в раскрывающийся элемент настроек, дав возможность войти в Windows Live из стандартной чудо-кнопки Settings (рис. 1 1.6). Глава 1 1. Работа с Live Connect API Sign in � Рис. 1 1.6. Вход из чудо-кнопки Settings Примечание. Я рассказывал об элементе управления SettingsFlyout в главе 6. В листинге 1 1.3 приведен НТМL-код для размещения элемента s i gni nSettings Flyout. Обратите внимание на элемент divSi gni n­
Control. Листинг 1 1.З. Файл si gnl nSetti ngsFl yout.html <!DOCTYPE html > <html > <head> <t i t l e>Securi ty</t i t le> <script type="text/j avascript" src ="s igninSett ings.j s"></script> </head> <body> <div id="divSignin" data-win-control ="WinJS.UI.SettingsFlyout" data-win-opt i ons ="{ > width: 'narrow' }" <div cl as s ="win-header" s tyle =" background- color: #4 6 4 6 4 6"> <button oncl ick="WinJS.UI.Sett ingsFlyout.show ( )" c l as s ="win-backbut ton"></butt on> <div cl as s ="win- l abel">Sign- in</div> </div> <div cl as s ="win-content"> <div id="divSigninContro l"></div> </div> </div> </body> </html> Элемент управления Signi n для входа в Windows Live создается в файле signI11Settings.js, который показан в листинге 1 1.4. Аутентификация пользователя Ли сти нг 1 1.4. Файл signl nSetti ngsFl yout.js ( funct i on ( ) { 'us e stri ct'; WinJS.UI.Pages.de f ine ("s i gninSett ings.html", { ready: funct i on ( el ement, opt i ons ) { } } ) ; } ( ) );
WL.ui ( {
name: н s igninн / el ement: "divSi gninControl", theme: "dark" } ) ; Элемент S ignin создается в методе WL. ui со следующими пара-
метрами: name - тип создаваемого элемента управления (в настоящее время единственный вариант - s ignin); element - элемент DOM, ассоциируемый с элементом управ­
ления; theme - тема: «l ight» (светлая) или «dark» (темная). Предупреждение. Если вы уже предоставили приложению право выполнять
единый вход, то элемент управления Si gni n не будет делать ничего полез­
ного, он просто нарисует кнопку Si gn out ( Выход), которая даст возможность выйти из системы. События аутентификации Live Connect API поддерживает следующие события: au th. login - возникает при входе в систему; auth.J.ogout - возникает при выходе из системы; auth. sessionChange - возникает при смене маркера доступа; auth.statusChange - возникает при изменении состояния пользователя; wl. log - возникает в случае ошибки при вызове службы Live. В примере ниже показано, как подписаться на события auth. login и auth. logout и выводить при этом различные сообщения. var spanResults = document.getElementByid ("spanResul t s"); WL. Event. subscribe ( • aut h. login", func t i on ( )
spanResul t s.innerText = "Произведен вход• ; Eml• • • •i Глава 1 1. Работа с Live Connect API } ) ; WL.Event.subscribe ("auth.l ogout •, funct i on ( ) spanResul t s.innerText = "Произ веден выход•; } ) ; Отметим, что для подписки на событие используется не метод ad­
dEventLis tener ( ),а метод Windows Live WL. Event. subs cribe ( ). Передача маркера ауте нтифи кации веб-сл ужбе Допустим, требуется вызвать удаленную веб-службу из приложения Магазина Windows. Например, вы пишете приложение для работы со списками задач, и веб-служба возвращает список задач пользователя, читая его из базы данных. Как аутентифицировать пользователя в веб-службе? Необходимо гарантировать, что один пользователь не может прочитать задачи другого. Нужен какой-то способ сообщить удаленной веб-службе, какой пользователь запрашивает задачи. Когда вы производите вход в систему с помощью Live Connect API, вы получаете маркер аутентификации. Этот маркер включает свертку секрета клиента (см. раздел «Регистрация приложения»). Примечание. Маркер аутентификации формируется по стандарту JSON Web Token ( JWТ), который подробно описан на странице http://tool s.i etf.org/ search/draft-j ones-json-weЬtoken-00. Затем полученный маркер аутентификации можно передать уда­
ленной службе. Служба может сама вычислить свертку секрета кли­
ента и сравнить ее с маркером аутентификации. Эта процедура позволяет безопасно идентифицировать пользо­
вателя, обратившегося к веб-службе. Давайте рассмотрим ее шаг за шагом. Отправка маркера аутентификации из приложения Магазина Windows Начнем с приложения Магазина Wi 11dows. В главе 8 «Создание ис­
точников данных» я показал, как можно создать источник, получаю­
щий данные от удаленной веб-службы. Воспользуемся этим источни­
ком данных еще раз. Передача маркера аутентификации веб-службе l l l • •ED НТМL-страница из листинга 1 1.5 содержит элемент управления Lis tView для отображения списка задач (рис. 1 1.7). Листинг 1 1.5. НТМL-страница для отображения списка задач
<div i d="tmplTask" data-win-control ="WinJS.Binding.Templ at e"> <div c l as s ="taskitem"> I d: < span dat a-win-bind="innerText:id"></span> <br /><br /> Name: <span dat a-win-bind="innerText:name"></span> <br /><br /> Dat e Created: <span data-win-bind="innerText:dateCreated"> '+</span> </div> </div> <div id="lvTasks" data-win-contro l ="WinJS.UI.Li s tView" data-win-opti ons ="{ i temTemplat e: sel ect ('#tmplTask'), s el ecti onMode: 'none' } "> </div> l d 1 Name Wake up Date Created 2012-07-ЗОТОб.ОО 00 Рис. 1 1. 7. Отображение списка задач Для получения списка задач от удаленной веб-службы служит код, приведенный в листинге 1 1.6 . В нем мы воспользовались источником данных, который был разработан в главе 8. Лис ти нг 1 1.6. Получение списка задач ( funct i on ( ) { "use stri ct 11; Глава 1 1 . Работа с Live Connect API var serviceURL = "ht tp://localhost:5 1 3 0 8/api/tasks"; var REDIRECT_DOMAIN = "http://l iveSDKDemo.Superexpert.com"; functi on init ( ) { WinJS.UI.proces sAl l ( ) .done ( funct ion ( ) { var lvTasks = document.getElementByid ("lvTasks") .winControl; //Инициализировать Live Connect var scopes = ["wl.s ignin"]; WL. init ( { scope: scopes, redirect_uri: REDIRECT_DOMAIN } ) ; //Войти в Live Connec t WL. l ogin ( ). then ( funct i on ( loginResul t s ) ) ; } ) ; 1 1 Привязать ListView к веб-спужбе, являющейся источником данных var tasksDataSource = new DataSources.WebServiceDataSource ( servi ceURL, "id" , l oginResul t s.s es s i on.authent i cat ion_token ) ; lvTasks.i temDataSource = tasksDataSource; } ' funct i on ( loginResponse ) spanResul t s.innerText = "Error when cal l ing WL.l ogin"; document.addEventLi s tener ("DOMCont entLoaded", ini t ); } ) ( ); Для входа в Windows Live от имени текущего пользователя ис­
пользуется метод WL. login ( ). После входа из свойства s e s s ion. authentication token читается маркер аутентификации, выданный пользователю службой Windovvs Live. Этот маркер нужен при создании экземпляра источника данных. Во всех методах источника удаленной службе отправляется маркер аутентификации. Например, в листинге 1 1.7 показан код метода getCount ( ).Обратите внимание, как маркер аутеатификации добав-
Передача маркера аутентификации веб-службе ляется в коллекцию заголовков, передаваемую методу WinJS. xhr ( ) в параметре options.
Листинг. 1 1.7. Метод get Count ( ) источника данных от веб-службы
getCount: funct i on ( ) { } ' var that = thi s; return new WinJS.Promi s e ( funct i on ( compl ete, error ) { var opt i ons = { url: that._url + "/getCount",
headers: { authent i cat i onToken: that. authent icati onToken } ; return WinJS.xhr ( opt i ons ) .then ( funct i on ( xhr ) { var count = JSON.pars e ( xhr.response );
complete ( count ); } ' funct i on ( xhr ) { cons ol e.l og ("Could not cal l getCount ( )"); debugger; } ) ; } ) ; Тот факт, что при каждом обращении к удаленной веб-службе нужно передавать маркер аутентификации, очень важен. Этот маркер идентифицирует пользователя, отправившего запрос. Проверка маркера аутентификации веб-службой Вторая часть процедуры обеспечения безопасности - проверить мар­
кер аутентификации на стороне веб-службы. Приложение Магазина Windows вызывает веб-службу Tasks, которая возвращает коллекцию задач. Сама веб-служба реализована с помощью технологии ASP.NET Web API. В частности, в ней имеется написанный на С# класс конт­
роллера с именем TasksController.
В классе Tasks Controller необходимо проверить маркер аутен­
тификации. По счастью, команда разработчиков Windows Live вклю­
чила в состав Live SDK пример класса, реализующего такую провер­
ку. Примечание. Скачать класс J s onWebTok en можно из своей учетной запи­
си на сайте Wi ndows Live Gi tHub по адресу https://github.com/l iveservi ces/ LiveSDK. Ищите на этой странице примеры для ASP.NET Глава 1 1 . Работа с Live Connect API В листинге 1 1.8 приведен код фильтра действия Authenti cation­
TokenAttribute, в котором используется класс JsonWebToken. Этот фильтр читает маркер аутентификации из заголовка запроса и про­
веряет его. Листинг 1 1.8. Фильтр действия AuthenticationTokenAttribute us ing System; using Sys t em.Col l ecti ons.Generic; us ing Sys tem.Linq; us ing Syst em.Web.Confi gurat i on; us ing JWTSampl e; namespace WebServi ces.Infrastructure puЫic cl as s Authent i cat i onTokenAttribute : Sys t em.Web.Ht tp. 4Fi l ters.Ac t i onFi l t erAt tribute { puЬl i c override voi d OnAct i onExecut ing ( System.Web.Http. 4Control l ers.Ht tpAct i onContext act i onContext ) { 1 1 Получаем маркер аутентификации Windows Live из заголовка string authent i cationToken = nul l ; try { authent i cationToken = act ionContext.Reques t.Headers. 4GetValues ( "authent i cationToken" ) . FirstOrDefault ( ); } catch { act ionCont ext.Response = new Sys t em.Net.Ht tp. 4HttpResponseMes s age ( System.Net.HttpStatusCode.Unauthori zed); } 1 1 Считываем секрет клиента из Web.conf i g var c l i entSecret = WebConf i gurat ionManager.AppSett ings 4 [ "CLIENT_SECRET"]; i f ( String.I sNullOrWhiteSpace ( c l i entSecret ) ) throw new Exception ("Missing Client Secret for Authentication"); 1 1 Проверяем маркер var d = new Dic t ionary < int, s tring> ( ); d.Add ( O, c l i entSecret ); try { var myJWT = new JsonWebToken ( authent icat i onToken, d); } catch { act ionContext.Response = new Sys t em.Net.Http. 4HttpResponseMes s age ( System.Net.HttpStatusCode.Unauthori zed); } Передача маркера аутентификации веб-службе l l l ••EИ Обратите внимание, что класс AuthenticationTokenAttribute читает секрет клиента (который вы получили, когда регистрировали свое приложение Магазина Windows) из файла Web.config. Предпо­
лагается, что вы поместили секрет клиента в секцию appSettings: <appSett ings> <add key="aspnet:Us eTaskFri endlySynchroni z ati onContext" '+value ="t rue" /> <add key="webpages:Vers i on" value="2.0.0.0" /> <add key="webpages:EnaЫed" value="fal s e" /> <add key="PreserveLoginUr l" value ="t rue" /> cadd key="Cl i entVal i dat i onEnaЫed" value ="t rue" /> <add key="Unobtrus iveJavaScriptEnaЫed" value ="t rue" /> cadd key="CLIENТ_SECRET" value="bj 0KdXm4W7 9ugL-Ed2ysy-0 9v8j RК6Wb" /> </appSett ings> Созданный класс AuthenticationTokenAttribute можно ис­
пользовать в качестве фильтра действия в любом методе контроллера ASP.NET Web API. Например, метод GetCount ( ) снабжается атрибу­
том AuthenticationTokenAttribute следующим образом: [ Ht tpGet ] [ Authent icati onToken] puЬl i c int GetCount ( ) return _db.Tasks.Count ( ); При попытке обратиться к любому действию контроллера TasksController, не передав правильный маркер аутентификации, действие вернет результат 401 Unauthoгized. Если запустить прило­
жение Магазина Windows под отладчиком и не указать маркер аутен­
тификации, то вы попадете в точку, показан ную на рис. 1 1.8. } ); }, гcti.r· r: \�!n:IS.xhr( o pt ia ns),thcn( ·j·".1�-tim 1 (хhг) { Vlf!" count • JSON. р11г:!:с(хhг .гсsрапsс); cvr.p l ctc ( c ou nt ); }, i'цщ;"ti1;щ { xhr} { consa l c.l ( E:< � �''' 1 dcbuggcг; .;, onlo.11drtart } ); ti,11 onprogrtc<;S .,_ onti111wut (,j OPENED null null null 1 tiil rradyStatl! .4 ite�sFromindcx: f;.,rн:ti<m '81 i�patщ! � • •• Vilr" th11t " �his; � responstТat О. ·"" til rcsponsrTypc а. • �· rctщ·n r:i..� Win:JS.P ro lliil r�p<iшrXMl null var url. th11t. ..-rtatus 4(>1 t "/itcmsf:-0 llii' rt.11tu1Tot а. ... �UniLrthoriztd" + �ica;.1nteet liill timwut {) + �1t:o!н1tAt1: lfl UNSEr-П О Э tiil uploid { ... } .,;;;с options ..., witПCrtdecntials fa1�e url: url, ,_ --•---
he 111ders: {authcnticAtionT oken: th111t ._ euthentic 111tionTokcn}-
Рис. 1 1.8. Запрос к веб-службе с неправильным маркером ауте нтификации Глава 1 1. Робота с live Connect API Извлечение идентификатора пользователя В маркере аутентификации, переданном веб-службе, есть несколько полезных полей: • ApplD - например, «00000000440CF1 1 2»; • Audience - например, «livesdkdemo.supeгexpeгt.com»; • Clientldentifier - например, «ms-app:/ /S- 1 - 1 5- 2-3755963394-
9949 1 985 1 - 1 648241 1 79- 1 781 31 831 9- 2690871 552- 1 993569707 -
2 1 42578964»; • Expiration - например, 7 /31/201 2 1 2:5 1:3 1 АМ; • expUnixTime - например, 1 343695891; • Issuer - например, «шn:windows:liveid»; • Userld - например, «ed63ad02ac2a852d56e2fa9886998532»; • Version - например, 1. Поле Us eri d особенно ценно, так как оно однозначно идентифи­
цирует пользователя и может использоваться для ассоциирования с ним данных в самой веб-службе. К сожалению, идентификатор поль­
зователя - это строка вида «ed63ad02ac2a852d56e2fa9886998532», а не имя или адрес электронной почты. В листинге 1 1.9 показано, как извлечь Us erI D из маркера аутенти­
фикации. Лис т инг 1 1.9. Изв лечение идентиф икатора поль зовател я 1 1 Получить идентификатор пользователя var authent icati onToken = Request.Headers.GetValues ._ ( "authent icat i onToken" 1. Fi rstOrDef ault ( ); var c l i entSecret = WebConf i gurat i onManager.AppSett ings '+ [ "CLIENT_SECRET"]; var d = new Di ct ionary<int, string> ( ); d.Add ( O, c l i entSecret ); var myJWT = new JsonWebToken ( authent i cat i onToken, d); var user = myJWT.Claims.Userid; Получение ос новной польз ователь с кой инфо рмации С помощью Live Connect API можно извлечь разнообразную поль­
зовательскую информацию, в том числе имя, дату рождения, адреса электронной почты и список друзей. В этом разделе я продемонстри­
рую, как для этой цели использовать метод WL. api ( ). Получение основной пользовательской информации 'll ll ••EJD Метод WL. api ( ) отправляет RЕSТ-запрос. При вызове ему пере-
даются четыре аргумента: path - URL-aдpec в стиле REST; method - НТТР-метод, например GET, POST, MOVE, СОРУ; Ьоdу - тело запроса ( сериализованное в формате JSON); type - используется только при создании папок или альбо­
мов. Наиболее важен аргумент ра th, потому что именно он определяет объект, с которым взаимодействует ваша программа. Например, чтобы получить фотографию пользователя, нужно задать путь «me/ picture». А чтобы установить сообщение о состоянии пользователя -
путь «me/shaгe:» (слово «me:» обозна<rает текущего пользователя). Примечание. Документация по всем REST URL, которые померживает Live Соппесt, находится по адресу http://msdп.mi crosoft.com/eп- us/l i brary/l ive/ hh243648.aspx. Я продемонстрирую, как работает этот механизм, на примере страницы, которая показывает некоторые сведения о пользователе (рис. 1 1.9). ИсходныйjаvаSсгiрt-код приведен в листинге 1 1.1 0. Рис. 1 1.9. Отображение пользовательской информации при помощи Live Conпect Листинг 1 1. 1 О. Получение основной пользовательской информации при помощи Live Connect ( funct ion ( ) { 11 use stri ct 11; var REDIRECT_DOMAIN = "http://l iveSDKDemo.Superexpert.com"; funct ion ini t 1 ) { var spanFirstName = document.getElementByid ("spanFirstName"); var spanLastName = document.getElementByid ("spanLastName"); 1Ш1•• 1 1� Глава 1 1 . Работа с Live Connect API var spanBirthday document.getEl ementByid ("spanBirthday"); var spanStatus document.getElementByid ("spanStatus"); var imgPhoto = document.getElementByid ("imgPhoto"); 1 1 Инициализируемм Windows Live var s copes = ["wl.s ignin", "wl.bas ic", "wl.Ьirthday"]; WL.init ( { scope: scopes, redirect_uri: REDIRECT_DOMAIN } ) ; //Входим в Windows Live WL.login ( ) .then ( funct i on ( l oginResul t s ) //Показываем основную информацию cal l LiveConnect ("me", "GET") .then ( funct i on ( resul t s ) spanFirs tName.innerText = resul t s.f irs t_name; spanLastName.innerText = resul t s.l as t_name; spanBirthday.innerText = resul t s.Ьirth_month + "/" + resul t s.Ьirth_day; } ) ; /! Показываем фотографию из профиля callLi veConnect ( "me/picture", "GET") . then ( funct ion ( results ) { imgPhot o.src = resul t s.locat i on; } ) ; } ) ; //Вызываем Live func t i on callLiveConnect ( path, method) { return new WinJS.Promi s e ( func t i on ( compl ete, error ) WL.api ( { path: pat h, method: method } ) . then 1 funct ion ( resul t s ) compl ete ( result s ); } ' funct i on ( resul t s ) } ) ; } ) ; //Ошибка при вызове WL.api ( ) debugger ; document.addEventLi stener ("DOMCont entLoaded", init ); } ) ( ); Здесь вся трудная работа выполняется в методе callLiveCon­
nect ( ), который вызывает метод WL. api ( ) для обращения к RЕSТ­
службе. Скачивание и закачивание файлов в SkyDrive Метод callLi veConnect ( ) вызывается дважды: в первый раз для получения основной пользовательской информации, а во второй -
для получения фотографии. Скачива ние и з акачивание файло в в SkyDri ve Технология Micгosoft SkyDгive позволяет хранить файлы в облаке и обращаться к ним с разных устройств. Любой житель Земли может бесплатно получить 7 гигабайтов в хранилище SkyDгive и радоваться жизни. Туда можно помещать фотографии, документы и вообще все что угодно. Live Connect API позволяет взаимодействовать с SkyDгive, в част­
ности закачивать, скачивать, копировать, перемещать и удалять фай­
лы. Допустим, вы хотите создать приложение для показа фотографий. Оно должно дать пользователю возможность просматривать свою фотогалерею с любого компьютера. В таком случае имеет смысл хра­
нить фотографии в SkyDгive. В этом разделе я объясню, как получить список папок и файлов пользователя в SkyDгive, а также как скачивать и закачивать файлы. Примечание. Для доступа к SkyDrive нужно перейти по адресу http:// SkyDrive.Live.com . Получение списка папок и файлов в SkyDrive Начнем с простого: как получить список папок и файлов пользователя в SkyDгive (рис. 1 1.1 0). Я буду отображать список папок и файлов в элементе управления Lis tView. НТМL-страница из листинга 1 1.1 1 содержит элементы уп­
равления Template и Li s tView. По шаблону показывается имя и тип каждого объекта, полученного от SkyDгive. Листинг 1 1.1 1. НТМL-страница для показа файлов, хранящихся в SkyDrive <div i d="tmplFi l e" data-win-control ="WinJS.Binding.Templat e"> <div cl as s ="f i l eit em"> Name: <span data-win-Ьind="innerText:name"><span> <br /> Туре: < span data-win-Ьind="innerText:type"><span> <div> EШ••lll Глава 1 1. Работа с live Connect API <div> <div id="lvFi l es" data-win-control ="WinJS.UI.Li s tView" dat a-win-opt i ons ="{ i t emTemplat e: s el ect ('#tmpl Fi l e'), sel ecti onMode: 'none' } "> </div> Рис. 1 1.1 О. Показ списка папок и файлов в SkyDrive JavaScгipt-кoд, показанный в листинге 1 1.1 2, получает список файлов и папок из корневой папки текущего пользователя в храни­
лище SkyDгive. Ли ст инг 1 1. 1 2. Код получения списка файлов и папок из SkyDrive ( funct i on ( ) { "use stri ct"; var REDIRECT_DOMAIN "ht tp://l iveSDKDemo.Superexpert.com"; funct i on init ( ) { WinJS.UI.proces sAl l ( ) .done ( funct i on ( ) { var lvFi l es = document.getElementByi d ("lvFi le s") .winControl; !! Инициализируем Live Connect var scopes = ["wl.s i gnin•, "wl.skydrive"]; WL.init ( { scope: scopes, redirect_uri: REDIRECT_DOMAIN } ) ; //Входим в Live Connect WL.login ( ) .then ( func t i on ( loginResul t s ) ( Скачивание и закачивание файлов в SkyDrive l l l ••EШ 1 1 Получаем список файлов в папке верхнего уровня callLi veConnect ( "me/skydri ve/ f i l es", "GET") . then ( funct i on '+( resul t s ) { var i tems = [ ]; for ( var key in resul t s.data ) { i t ems.push ( resul t s.data [ key] ); var ds i tems = new WinJS.Binding.Li s t ( i t ems ); lvFi l es.i t emDat aSource = ds i t ems.dataSource; } ) ; } ) ; } ) ; //Обращаемся к Live Connec t funct i on cal l LiveConnect ( path, method) { return new WinJS.Promi s e ( funct i on ( complete, error ) { WL.api ( { path: path, method: method } ) .then ( ) ; } ) ; funct ion ( result s ) complet e ( result s ); } ' funct i on ( resul t s ) { //Ошибка при вызове WL.api ( ) debugger; document.addEventLi s tener ("DOMCont entLoaded", ini t ); } ) ( ); В первой части этого кода мы инициализируем подключение к Live Connect, вызывая метод wl. init ( ) с контекстом wl. s kydrive. Для чтения файлов и папок из SkyDгive пользователь должен дать разре­
шение wl. s kydrive, а для обновления - разрешение wl. s kydrive_ update. Затем мы входим от имени пользователя, вызывая метод wl. login ( ).Далее мы получаем файлы и папки из области пользова­
теля в SkyDгive, обращаясь к методу callLiveConnect ( ),которому передаем путь «me/skydгive/files». Список файлов и папок возвращается в свойстве da ta. Как ни странно, этот список представляет собой не мaccив JavaScгipt, а объ­
ект. Поэтому мы преобразуем его в массив следующим образом: EШ•• l l l for ( var key in resul t s.data ) i t ems.push ( resul t s.data [ key] ); Глава 1 1. Работа с Live Connect API И наконец, массив i tems преобразуется в источник данных Li s t, который привязывается к элементу управления Li s tView, где и отоб­
ражаются файлы и папки. Скачивание файлов из SkyDrive Для скачивания файлов из области пользователя в SkyDгive приме­
няется метод WL. backgroundDownload ( ) , которому передается путь к исходному файлу и к его копии на локальном диске. Я изменю элемент Li s tView, который мы рассматривали в пре­
дыдущем разделе, так чтобы он поддерживал скачивание файлов. При щелчке по файлу в Li s tView появится экран, на котором бу­
дет предложено сохранить файл из SkyDгive на локальном диске (рис. 1 1.1 1 ). Рис. 1 1. 1 1. Сохранение файла из SkyDrive Для начала нужно изменить параметры Li s tView, так чтобы он генерировал событие i teminvoked при щелчке или касании элемента списка. <div id="lvFi l es" dat a-win-control ="WinJS.UI.Li s tView" data-win-opt i ons="{ i temTemplate: select ('#tmplFi l e'I, t apBehavior: 'invokeOnly' }"> <div> Затем я должен реализовать обработчик события i teminvoked, в котором выбранный файл сохраняется на диске (листинг 1 1.1 3). Скачивание и закачивание файлов в SkyDrive •••••&D Листи нг 1 1. 1 3. Обработчик события i teminvoked //Настроить обработчик lvFi l es.addEventLi s tener ("i t eminvoked", funct i on ( evt ) evt.detai l.i temPromi s e.done ( funct i on ( invokedi tem) { var i t emData= invokedi tem.data; // Папки и альбомы н е скачиваем i f ( i t emDat a.type== "folder" 1 1 i t emDat a.type== "album") { return ; // Создаем элемент для настройки сохранения var savePicker= new Windows.Storage.Pi ckers.Fi l eSavePi cker ( ); savePi cker.sugges t edStartLocat i on= Windows.St orage.Pi ckers. '+Pi ckerLocat i oni d.document sLibrary; savePi cker.suggestedFi l eName= i t emDat a.name; s avePi cker. f i leТypeChoi ces. insert ( "PNG f i l e", [" .png"] ); savePicker. f ileТypeChoices. insert ( "JPEG f ile", [". j pg", ". jpeg"] ) ; s avePi cker.f i l eТypeChoi ces.insert ("Micros of t Word Document", '+(" .docx", ".doc"] ); //Отображаем этот элемент savePi cker.pickSaveFi leAsync ( ) .then ( funct i on ( f i l e ) { i f ( f i l e ) { } } ) ; } ) } ) ; WL.backgroundDownl oad ( { pat h: it emDat a.id + "/content", f i l e_output: f i l e } ) ; При возникновении события i teminvoked код, показанный в лис­
тинге 1 1.1 3, получает выбранный в списке Li s tView элемент с помо­
щыо метода i temPromi s e ( ). Затем для сохранения выбранного файла создается элемент FileSavePicker. Он настраивается для обработки файлов типа PNG, JPEG и документов Micгosoft Woгd. Если вы хотите разрешить скачи­
вание файлов других типов, добавьте их в коллекциюfilеТуреСhоiсеs элемента FileSavePicker. Для показа экрана сохранения файла вызывается метод pickSaveFileAsync ( ). Когда пользователь нажимает кнопку Save, вызывается метод Live Connect WL. backgroundDownload ( ) . Этот ме­
тод скачивает файл из SkyDгive и сохраняет его там, где указал поль­
зователь. Глава 1 1. Работа с live Connect API Обратите внимания на путь к месту сохранения файла: itemDa­
ta. id + "/content". Чтобы получить содержимое (а не описание) файла, нужно будет указать его идентификатор, за которым следует строка «/co11te11ti>. Закачивание файлов в SkyDrive Ну а как закачать файл в SkyDгive? Перед этим вы должны получить соответствующее согласие от пользователя. Подключение к Live API инициализируется с контекс­
том wl. s kydrive update: 11 Инициализируем Live Connect var scopes = [ "wl. s i gnin", "wl. skydrive_update"]; WL.init ( { scope: scopes, redirect_uri: REDIRECT_DOMAIN } ) ; Чтобы поддержать закачивание файлов, я еще раз модифицирую приложение, написанное в предыдущем разделе, включив в него па­
нель с командой скачивания (рис. 1 1.1 2). Эта панель объявляется следующим образом: <div data-win-control ="WinJS.UI.AppBar"> <button data-win-cont rol ="WinJS.UI.AppBarCommand" data-win-opti ons ="{ i d: 'cmdUpload', l abel : 'Upl oad' , icon: 'upl oad', tool tip:'Upload Fi l e' } "> </button> </div> И наконец, нужно написать код, обрабатывающий команду зака­
чивания. Он приведен в листинге 1 1.1 4. Листинг 1 1 • 1 4. Обработка команда закачивания //Настроить обработчик команды upl oad var cmdUpl oad = document.getElementByi d ("cmdUpload"); cmdUpload.addEventLi s t ener ("c l i ck", funct i on ( ) { var openPi cker = new Windows.Storage.Pickers.Fi l eOpenPicker ( ); openPicker.f i leTypeFi l ter.replaceAl l ( [ "*" ] ) ; openPi cker.pi ckSingl eFi l eAsync ( ) .then ( funct ion ( f i l e ) { WL.backgroundUpload ( { path: "me/skydrive", Резюме f i l e_name: f i l e.name, f i l e_input: f i l e } ) . then ( funct i on ( ) { get Fi leLi s t ( ); } ) ; } ) ; } ) ; Рис. 1 1. 1 2. Панель приложения с командой Upload Этот код отображает экран выбора файла с диска. После того как файл выбран, вызывается метод Live Connect WL.background­
Upload ( ),которому передаются три параметра: path - путь в хранилище SkyDгive, куда нужно поместить за­
качанный файл; file_name - имя файла в хранилище SkyDгive; file_input - файл, выбранный на экране выбора. По завершении закачивания вызывается метод getFi leLi s t ( ), чтобы обновить список файлов в элементе Li s tView, в котором те­
перь должен появиться и вновь закачанный файл. Рез юме В этой главе мы обсудили вопрос об интерфейсе Live Connect API. Вы научились использовать его для организации единого входа без ввода данных. Это позволяет аутентифицировать пользователя, не m1•• 1111 Глава 1 1. Работа с live Connect API
заставляя его вводить имя и пароль. Вы также узнали, как приложе­
ние Магазина Windows может передать маркер аутентификации уда­
ленной веб-службе. Затем вы узнали, как получить доступ к сокровищнице информа­
ции о текущем пользователя от службы Live Seгvices. В частности, я продемонстрировал, как извлечь имя, фамилию, дату рождения и фотографию пользователя. Наконец, я объяснил, как обращаться к облачному хранилищу SkyDгive из приложения Магазина Windows. Вы научились получать список файлов, а также закачивать и скачивать файлы. В этой главе: • Описание игры.
rЯ АВ А 12. rр афи ка и и rры • Создание плиток для игры.
• Звуковое сопровождение игры.
• Создание холста для игры.
• Взаимодействие с пользователем.
• Цикл обновления.
• Цикл рендеринга.
В этой главе мы на примере простой игры рассмотрим, как пишутся игровые приложения Магазина Wiвdows. Наша игра будет называть­
ся «Пожиратели мозга». Цель игры - съесть вкусняшки и при этом не быть самому съеден­
ным зомби. Если удастся съесть все пять вкусняшек, вы выиграли. Если вас съел зомби - проиграли (рис. 1 2.1 ). Игра работает с клавиатурой, мышью, сенсорным экраном и сти­
лосом. Перемещать персонаж можно клавишами со стрелками. На планшетном компьютере можно просто двигать по экрану пальцем в направлении перемещения персонажа. Играть можно на экранах с разным разрешением. Размер игрового поля автоматически подстраивается под размер экрана. Примечание. Полный исходный код игры находится в папке Chapter1 2\
Game. Глава 1 2. Графика и игры Рис. 1 2.1. Игра «Пожиратели мозга» Описа ние игры Для игры «Пожиратели мозга�,> я воспользовался шаблоном Visual Studio Navigatio11 Арр ( Приложение навигации). Игра состоит из четырех страниц. Начальная - здесь приведены правила. Чтобы перейти к стра­
нице иг ры, нуж н о на ж ать кн оп ку Staгt Gа ше ( На чать игру). Игра - здесь находится сама игра, вы убегаете от зомби. Проигрыш - если зомби схватил вас, игра проиграна, и вы попадаете на эту страницу. Что бы вернуться на страницу игры, нужно нажать кнопку Play Agai11? ( Сыграть еще раз?). Выигрыш - если вы съели все вкусняшки, то игра выиграна, и вы попадаете на эту страницу. Начальная страница, а также страницы выигрыша и проигрыша не представляют особого интереса - это обычные НТМL-страницы со ссылками на страницу игры (рис. 1 2.2). Само действо происходит на странице игры. Почти весь код игры находится в JavaScгipt-фaйлe game. j s - в классе Game. Этот класс содержит код начала и завершения игры, из­
менения положений зомби и рендеринга игрового поля. Создание плиток для игры Рис. 1 2.2. Начальная страницы игры «Пожиратели мозга» Создание плито к для игры Игра «Пожиратели мозга» составлена из набора плиток с изобра­
жениями размером SOxSO пикселей. Изображение игрового поля состоит из плиток фона и стен. Имеются также плитки зомби, игрока и вкусняшек. Все игровые плитки создаются в файле tiles.js (листинг 1 2.1 ). Лис ти нг 1 2. 1. Файл ti l es.j s ( funct i on ( ) { 11 use stri ct 11;
funct i on Ti l e ( url ) thi s.image = new Image ( );
thi s.image.src url; var t i l es = { };
t i l es.background = new Ti l e l"/images/background.j pg"); t i les.wal l = new Ti l e ("/images/brick.j pg"); m1••111 Глава 1 2. Графика и игры t i les.player = new Ti l e ("/images/hero.png"); t i les.z omЬie = new Ti l e ("/images/zomЬie.j pg"); t i les.hamburger = new Ti l e ("/images/hamburger.gi f"); WinJS.Namespace.de f ine ("Unleashed", Ti l es: t i l e s } ) ; } ) ( ); Каждая плитка представлена графическим файлом, хранящимся в папке images. Например, плитка с изображением стены находится в файле bгick.j pg. В игре используются изображения в форматах PNG, GIF и JPG. Вообще, годятся любые графические форматы, поддерживаемые сов­
ременными браузерами. Набор плиток раскрывается в виде свойств объекта Unleashed. Tiles. Например, получить плитку стены можно с помощью свойства Unleashed.Ti l es.wall. З ву ковое с опровожд ение игры Когда вас пожирает зомби, в ы умираете довольно шумной смертью. А 1югда съедаете вкусняшку, раздается красноречивое <:<Ам!�>. Все звуки находятся в файле souвds.j s (листинг 1 2.2). Листинг 1 2.2. Файл sounds.js ( funct ion ( ) { "use stri ct";
WinJS.Namespace.de f ine ("Unleashed", Sounds: { } } ) ; } ) ( ); yum: new Audio ("/sounds/yum.wav"), eaten: new Audio ("/s ounds/eat en.wav"),
cheer: new Audio ("/s ounds/cheer.wav") Звуки представлены файлами в формате WAV. Я записывал их с помощью бесплатного звукового редактора Audacity с открытым исходным кодом. Звуки раскрываются в виде свойств объекта Unleashed. Sounds. Например, для воспроизведения звука <:<Al\'11> нужно выполнить такой код: Unleashed.Sounds.yum.pl ay ( ); Создание холста для игры Примечание. Звукам, издаваемыми зомби, я обязан своим детям - Иону, Аде и Афине. Соз дание хол ста для игры Графика игры отрисовывается с помощью элемента HTML S Canvas (холст). Этот элемент создается следующим образом: <div data-win-control ="WinJS. UI. Vi ewBox"> <canvas id="canvas" width="1 0 0 0" height ="7 5 0"></canvas> </div> Обратите внимание, что элемент Canvas погружен в элемент управления WinJS ViewBox, который автоматически масштабирует Canvas с учетом разрешения экрана. Благодаря ему можно с равным успехом играть на экране 1 024х768 пикселей в книжном режиме (рис. 1 2.3) или на экране 256Ох1 440 пикселей в альбомном режиме (рис. 1 2.4). Рис. 1 2.3. Игра на экране 1 024х768 пикселей в книжном режиме Глава 1 2. Графика и игры Рис. 1 2.4. Игра на экране 256Qx1 440 пикселей в альбомном режиме В объявлении элемента Canvas указан размер 1 00Ох750 пикселей. Все плитки игры - стены, зомби и другие - рисуются в пределах этого элемента Canvas. При инициализации класса Game в файле game.js мы получаем от элемента Canvas двумерный контекст: 1 1 Настроить Canvas this._canvas = document.getEl ementByi d {"canvas"); this._ctx = this._canvas.getContext {"2 d"); Двумерный контекст содержит графический API для рисования на холсте Canvas. Этот API содержит, в частности, методы l ineтo ( ), rect ( ) и arc ( ) для рисования отрезков прямых, прямоугольников и дуг соответственно. В игре «Пожиратели мозга�> используется только один метод Canvas API: drawimage ( ). Он предназначен для рисования изобра­
жения в указанном месте холста. Например, плитка игрока рисуется так: renderPlayer: funct ion { ) { thi s.drawimage { this._pl ayer.t i l e.image, this._pl ayer.x, '-thi s._player.y ); } ' drawimage: funct i on { image, х, у) { this._ctx.drawimage { image, х * TILE_WI DTH, у * TILE_HEIGHT );
Взаимодействие с пользователем ••• •• 1111 Метод renderPlayer ( ) обращается к методу drawimage ( ),переда­
вая ему координаты х и у. А метод drawimage ( ) рисует изображение на холсте, вызывая метод drawimage ( ) его двумерного контекста. Примечание. К сожалению, метод контекста drawimage ( ) не позволяет ри­
совать анимированные изображения в формате GI F. Согласно спецификации HTML5, «если методу drawl mage( ) передается анимированное изображение в аргументе i mage, то агент пользователя должен нарисовать заглавный кадр, а, если такового нет, то первый кадр». Облом! Вз аимодействие с поль з ов ате лем В игру «Пожиратели мозга» можно играть с помощью клавиатуры, сенсорного экрана или стилоса. В случае клавиатуры персонаж пере­
мещается клавишами со стрелками. Ниже показано, как реализуется интерфейс с клавиатурой: document.addEvent Li s t ener ("keydown", this.movePlayerKeyboard . ._.Ьind 1 thi s ) ); При нажатии клавиши вызывается метод movePlayerKeyboard ( ), который анализирует, какая клавиша нажата и изменяет направление движения персонажа. movePlayerKeyboard: funct i on ( е ) { swi tch 1 е. keyCode ) { } } ' case WinJS.Ut i l i t i es.Key.upArrow: thi s._pl ayer.direct i on = Unleashed.Direct i on.up; break; case WinJS.Ut i l i t i es.Key.downArrow: this._player.direct i on = Unl eashed.Direct i on.down; break; case WinJS.Ut i l it i es.Key.l e ftArrow: this._player.direct i on = Unleashed.Direc t i on.l ef t; break; case WinJS.Ut i l i t i es.Key.rightArrow: thi s._player.direct i on = Unl eashed.Direc t i on.right; break; case WinJS.Ut i l it i es.Key.space: thi s._player.direc t i on = Unl eashed.Direc t i on.none; break; Метод movePlayerKeyboard ( ) определяет, какая клавиша нажата, сравнивая ее код с элементами перечисления WinJS. Utili ties. Кеу. Это перечисление содержит понятные человеку коды клавиш, так что можно использовать символическое обозначение WinJS. Utilities. Кеу. upArrow вместо числа 38. Глава 1 2. Графика и игры При работе с планшетным устройством вместо клавиатуры ис­
пользуются жесты касания. Ниже показано, как обрабатывается со­
бытие MSPointerDown, которое генерируется, когда пользователь на­
жимает кнопку мыши либо касается экрана пальцем или стилосом. this._canvas.addEventLi s tener ("MSPoint erDown", thi s.movePlayerTouch.Ьind ( thi s ) ); При возникновении события MSPointerDown вызывается метод movePlayerTouch ( ),который изменяет направление движения пер­
сонажа. movePlayerTouch: funct ion ( е ) { var playerX = this._player.x * TILE_WI DTH; var playerY = this._player.y * TILE_HEIGHT; var absX Math.abs ( e.o f f setX pl ayerX ); var absY = Math.abs ( e.o f f setY - pl ayerY); i f ( absX > abs Y) { } ' i f ( e.o f f setX > playerX) thi s._pl ayer.direct i on el s e { this._pl ayer.direct i on el s e { i f ( e.o f fsetY > pl ayerY) this._pl ayer.direct i on el s e { this._player.direct ion Unl eashed.Direc t i on.ri ght; Unl eashed.Direc t i on.l ef t; Unl eashed.Direct i on.down; Unleashed.Direct i on.up; Новое направление зависит от того, в каком месте экрана про­
изошло касание. Если коснуться экрана ниже персонажа, то он на­
чнет двигаться вниз, если правее - то вправо (рис. 1 2.5). Рис. 1 2.5. Каса ние экрана для изменения направления Цикл обновления l l l • • EШ Цикл обновл ения В игре «Пожиратели мозга» исполняются два цикла. В ЦИ1СЛе обновле­
ния вычисляются новые положения игрока и зомби, а в цикле ренде­
ринга производится отрисовка нового состояния игрового поля. Цикл обновления исполняется раз в 250 миллисекунд. Метод executeUpda teLoop ( ) вызывается в ответ на события таймера, настроенные при помощи метода window. s etinterval ( ) в начале игры: thi s._updateLoopid = window.s et int erval ( thi s.execut eUpdateLoop. '+bind ( thi s ), UPDATE_LOOP_RATE );
Примечание. Метод Ьi nd ( ) объекта execut eUpda t eLoop ( ) нужен
для привязки контекста к текущему экземпляру Game во время испол­
нения метода execut eU pd at eLoo p ( ). Иными словами, внутри метода execut eUpda t eLoo p ( ) переменная this будет указывать на текущий экземп­
ляр Gаmе. Константа UPDATE_LOOP_RATE равна 250 миллисекунд. Если вы
хотите, чтобы зомби преследовали персонажа быстрее, можете умень­
шить это значение (играть при этом, конечно, станет труднее). Метод executeUpdateLoop ( ) выглядит следующим образом: executeUpdat eLoop: funct i on ( ) { thi s.updateMonsterPos i t i ons ( ); thi s.updatePlayerPos i t i on ( ); } ' Он вычисляет новые положения зомби и игрока. Например, метод updateMons terPos i tions ( ) перемещает всех зомби в направлении к игроку. Во время обновления положений зомби и игрока проверяются также столкновения. Мы должны знать, произошло ли столкновение между игроком и зомби или между игроком и вкусняшкой. Чтобы понять, наткнулся ли игрок на вкусняшку, ме-
тод updatePlayerPos i tion ( ) вызывает следующий метод col lideWithFood ( ):
col l i deWithFood: funct i on ( ) { for ( var i = О; i < thi s._food.l engt h; i ++ ) { var food = this._food [ i ]; i f ( thi s._player.x === food.x && this._pl ayer.y Unleashed.Sounds.yum.pl ay ( );
thi s._food.spl i ce ( i, 1 ); food.y ) { Глава 1 2. Графика и игры !/Если еды больше не осталось, игрок выиграл i f ( this._food.l ength === 0 ) { } } ' this. win ( ) ; Метод collideWi thFood ( ) в цикле обходит массив _ food, про­
веряя, совпадают ли координаты вкусняшки с координатами игрока. Если это так, воспроизводится звук «Ам» и вкусняшка удаляется из массива методом _ food. splice ( ). Если все вкусняшки съедены, иг­
рок выигрывает. В цикле обновления на экран ничего не выводится. Он отвечает только за изменение состояния игры. Отрисовка игрового поля - обя­
занность цикла рендеринга. Цикл ренде ринга Цикл рендеринга отвечает за отрисовку игрового поля. В отли­
чие от цикла обновления, который срабатывает в ответ на события таймера, цикл рендеринга исполняется в результате вызова метода reque s tAnimationFrame ( ). Метод reques tAnimationFrame ( ) определен в стандарте WЗС уп­
равления хронометражем для скриптовой анимации (Timing Contгol fог Scгipt- Based Animation). Этот метод специально предназначен для создания анимированных игр. При использовании метода reques tAnimationFrame ( ) вы не ука­
зываете, как часто должен обновляться экран, а предоставляете брау­
зеру определить оптимальную частоту. Идея в том, что браузеру виднее, какова должна быть частота об­
новления кадров, поскольку он может учесть все текущие анимации и, следовательно, сделать анимацию более плавной. Если же страница не видна, то браузер может притормозить анимацию и тем уменьшить нагрузку на процессор. В игре «Пожиратели мозга» цикл рендеринга начинается так: thi s._animati onLoopid = window.requestAnimat i onFrame ( thi s. '+executeRenderLoop.bind ( thi s ) ); Метод executeRenderLoop ( ) передается методу reque s tAnima­
tionFrame ( ). Сам же метод executeRenderLoop ( ) выглядит следу­
ющим образом: Цикл рендеринга 111 •8ЕШ execut eRenderLoop: funct i on ( ) { thi s.render ( ); this._animat i onLoopid=window.requestAnimat i onFrame ( thi s. '+executeRenderLoop.bind ( thi s ) ); } ' Метод executeRenderLoop ( ) вызывает метод render ( ) и сразу после этого снова обращается к методу window. reques tAnimation­
Frame ( ), который решает, когда вызвать executeRenderLoop ( ) в следующий раз. Примечание. Метод window. reques tAnimationFrame ( ) похож на метод window. setTimeout ( ) с тем существенным отличием, что величина таймау­
та не задается - reques tAnimationFrame ( ) вычисляет ее самостоятельно . . Метод render ( ) отвечает за отрисовку экрана: render: funct i on ( ) { thi s.renderBoard ( ); this.renderFood ( ); thi s.renderMonsters ( ); thi s.renderPlayer ( ); } ' Этот метод отрисовывает игровое поле, вкусняшки, монстров и игрока. Метод renderBoard ( ) рисует игровое поле размером 2Ох 1 5 пли­
ток, вызывая метод drawimage ( ) для каждой плитки. Ниже приведен его код. renderBoard: funct i on ( ) { for ( var у = О; у < VERTICAL_TILES; у++ ) { } } ' for ( var х = О; х < HORIZONTAL_TILES; х++ ) var t i l e = this.getTi l e ( x, у ); i f ( t i l e ) { thi s.drawimage ( t i l e.image, х, у ); el s e { this.drawimage ( Unl eashed.Ti l es.background.image, х, у ); Методы renderFood ( ) и renderMonsters ( ) очень похожи: тот и другой обходят массив и рисуют на экране все встретившиеся эле­
менты. Вот как выглядит метод renderMonsters ( ): renderMonsters: func t i on ( ) { for ( var i = О; i < thi s._monsters.l ength; i ++ ) } ' Глава 1 2. Графика и игры var monster = this._mons ters [ i ]; thi s.drawimage ( mons ter.t i l e.image, mons ter.x, monster.y ); Здесь массив _monsters содержит объекты, представляющие монстров. Для рисования каждого монстра на холсте вызывается ме­
тод drawimage ( ).
Наконец, метод renderPlayer ( ) рисует игрока (главного героя игры): renderPlayer: funct i on ( ) { this.drawimage ( thi s.__pl ayer.t i l e.image, this._player.x, '+thi s._pl ayer.y ); } ' Примечание. Спецификацию метода reques tAnimationFrame ( ) мож­
но найти по адресу http://dvcs.wЗ.org/hg/webperf/raw-fi l e/ti p/specs/ RequestAn i mati on Frame/Overvi ew. htm 1. Р ез юме В этой главе мы разработали простую игру в виде приложения Ма­
газина Windows. Я показал, как создать изображения и звуки для игры и как выполнить рендеринг изображений с помощью элемента HTML S Canvas. Вы также научились писать циклы обновления и рендеринга. В цикле обновления вычисляются новые положения монстров и иг­
рока на игровом поле. В цикле рендеринга игровое поле перерисовы­
вается благодаря вызову метода reques tAnimationFrame ( ). Играйте и выигрывайте! Если выиграете, услышите, как смеются мои дети. П Р ЕД М ЕТ Н ЫЙ УК АЗАТ ЕЯЬ
А автоматич еское исчезновение 1 71 адаптер данных, создание 231 альбомная ориентация 270 асинхронное программирование 69; обещания 70; композиция 75; методы theп() и done() 71; отмена 75; создание 73; таймауты 7 4; обратные вызовы 69 атрибуты, контроль данных в формах HTML 151; pattern 152; required 152 аутентификация пользователей; маркеры аутентификации; передача веб-службе 308; проверка веб-службой 311; подключение к службе Live Conпect; метод WL.login() 304; элемент управления Signln 305; события аутентификации 307 в внешние шаблоны, создание 1 17 д декларативная привязка к данным 99; и наблюдаемые объекты 102; и элементы управления Wiп J S 106; конвертеры привязки 108 декларативные шаблоны 1 14 диалоговые окна, отображение 190 диапазоны ключей 250 Е единый вход без ввода данных 304 закрытие приложения Магазина Wi пdows 26 закрытые методы 61 и императивные шаблоны 1 12 индексы 250 индикатор хода выполнения 167 инкапсуляция методов 61 ИСТО ЧНИКИ данных; база данных IndexD B как 247; веб-служба как 242; нестандартные, создание. См. нестандартные источники данных, создание; файл как 237 к клавиатура, поддержка 21 классы 63; Vir tual izedDataSouгce 231; наследование 65; определение, метод Wiп JS.Cl ass. defiпe( ) 63 книжн ая ориентация 270 коллекции наблюдаемые 96 конвертеры привязки 108; для преобразования даты и денежной суммы 1 1 1 контекстно-зависимые команды 182 контекстного масштабирования; переключение представлений в элементе управления ListView 219 контексты для приложения Магазина Wi пdows, задание 301 контроль данных в форме (HTML5); атрибут patterп 152; атрибут гequiгed 152; настройка стиля сообщения об ошибке 155; нестандартный 153; сброс формы 156 м маркеры аутентификации; извлечение идентификатора пользователя 314; передача веб-службе 308; проверка неб-службой 3 1 1 метки полей формы 158 методы; сЬапgе(), реал изация 235; getCouпt(), реализация 232; IListDa taAdapteг, интерфейс 231; iпseгtAtEпd( ), реализация 234; mediaMatc Ь(), реализация 27 4; гешоvе(), реализация 234; setNotШcatioпHaпdleг(), реализация 236; tЬеп() и dопе( ), сравнение 71; timeout() 74; \Viпdow.setiпteгval ( ) 7 4; Wiпj S. Class.defiпe( ) 63; Wiп jS.Cl ass.deгive() 65; Wiп JS.Class.mix() 67; Wi11 JS.Naшespace.defi11e() 58; Wi11 JS.Namespace.deH11e Witl1Paгe11t( ) 60; Wi11J S.U tilities.cl1ildгe11() 80; Wiп JS.Utilitie s.id() 79; Wiп J S.Utilities.queгy() 77; WL.a pi () 314; WL.l ogiп{ ) 304; инкапсуляция 61; класса Queгy CoJlectioп 80 Предметный указатель методы установки 68 методы чтения 68 многостраничные приложения, создание 285; с навигацией 286 множественное наследование, поодержка с помощью примесей 67 модальные диалоги, отображение 190 мышь, поддержка 2 1 н наблюдаемые объекты; и декларативная привязка к данным 102; коллекции 96; прослушиватели, создание 92; создание 90; уведомления; обход отправки 95; объединение 93 навигации API 292; состояние навигации 293 навигация по страницам 291 нестандартные источники данных, создание; метод getCouпt( ), реализация 232; метод iпseгtAtEп d( ), реализация 234; метод iteшsFгoшiпdex( ), реал изация 232; метод setNotificatioпHaпd leг(), реализация 236; обработка ошибок 235; создание адаптера данных 231 нестандартные события приложения, создание 263 о обещания; композиция 75; откладывание событий с помощью 263; отмена 75; создание 73; создание обещания-таймаута 7 4; сцепление 72 Предметный укзатель область видимости переменной 61 обозреватель модели DOM 50 обработка ошибок; в нестандартных источниках данных 235; методы tl1en() и done() 71 обратные вызовы 69 объединение уведомлений 93 объекты; добавление в хранилище 249; наблюдаемые; и декларативная привязка к данным 102; создание 90 окно просмотра, определение 275 О программе, страница, создание 185 опрос носителя 36; адаптация к состоянию просмотра 272; с помощью метода mediaMatc\1() 27 4 открытые методы 61 отладка приложений Магазина Wi ndows 48; кoнcoльJava Script в Visual Studio 49; обозреватель модели DOM 50; точки останова 50 п панель навигации 23 панель приложения 22; команды 179; контекстно-зависимые команды 182; создание 177 персональные настройки приложения Маг азина Wi ndo\vs, создание 187 Пожиратели мозга, игра; взаимодействие с пользователем 331; звуковое обновления 333; звуковое сопровождение 328; создание плиток 327; создание холста 329; страницы 326; цикл рендеринга 334 привязка к данным; декларативная 99; и элементы управления WinJS 106; 111 ••Ш1 получение содержимого НТМL ­
формы 104; конвертеры привязки 108 приложения; многостраничные, создание 285; с навигацией 286; отправка в Магазин Wi пdo\VS для публикации 53; предыдущее состояние выполнения, определение 265; приостановка, обнаружение 264; публикация, процедура сертификации 54; разрешения, отзыв 303; регистрация 300; скеоморфизм 21; состояние, хранение в состоянии сеанса 267; состояние просмотра 269 приложения Ма газина Wi пdows 18; задание контекстов 301; закрытие 26; запуск 34; общие характеристики 21; объявление возможностей 28; отладка. См. отладка приложений Магаз ина Wi пdows; отправка для публикации 53; панель навигации 22; панель приложения 22; персональные настройки, создание 187; предыдущее состояние выполнения, определение 265; приостановка, обнаружение 264; регистрация 300; события приложения. См. события приложения; создание НТМ L-страницы 30; созданиеJаv аSсriрt-файла 32; создание проекта 26; создание таблицы стилей 31; состояние, хранение в состоянии сеанса 267; состояния просмотра. См. состояния просмотра; среда выполнения Wi пdows 36; ED•• l l � страница "О программе", создание 185; чудо-кнопки 23; элементы. См. элементы приложения Магазина Wi ndo\vs приложения с навигацией; Page Coпt гolNavigatoг, элемент управления 293; переход на другую страницу 291; создание 286; страничные элементы управления, добавление 288 примеси, создание 67 принципы стиля оформления Майкрософт 19 прослушиватели, создание 92 пространства имен 58; определение; метод Wiп J S.Naшespace.defiпe() 58; метод WiпJS.Naшespace.defiпe Witl1-
Paгent( ) 60 прототипическое наследование 65 публикация приложения в Магазине Wi пdows 52; отправка 53; сертификация 54 р рабочие неб-процессы 35 разрешающая способность; изменение в эмуляторе Visual Studio 272; определение окна просмотра 275 разрешения, отзыв 303 регистрация; в качестве разработчика Магазина Wiпdo\vs 52; приложения 300 редактор обогащенного текста, создание 166 с селекторы запроса 76; и шаблоны 1 16; класс QueгyCollectioп 80; Предметный указатель метод Win J S.Ut ilities.childгen() 80; метод Win J S.Utilitie s.id() 79; метод Wiп JS.Ut ilities.queгy( ) 77; синтаксис 77 сертификация приложений Магазина Wi пdo\vs 54 сеточный макет (элемент управления ListView) 199 скеоморфизм 21 службы Live 298 события приложения 259; activated, обработка 261; нестандартные, создание 263; откладывание событий с помощью обещаний 263 состояние сеанса 267 состояния просмотра 25, 269; автоматическая адаптация с помощью опроса носителя 272; альбомное 270; заполняющее 269; книжное 270; определение окна просмотра 275; прикрепленное 269 списковый макет (элемент управления ListView) 199 среда выполнения Wi ndows (W inRT) 36 стилос, поддержка 21 страничные элементы управления; добавление в приложение с навигацией 288; создание 282 т таблица стилей, создание для приложения Магазина Wi пdows 31 таймаут, обещание 7 4 точки останова, отладка приложения Магазина Wi пdows 50 ф форматирование; времени (элемент управления TiшePi ckeг) 142; Предметный укзатель даты (элемент управления DatePickeг) 135 формы; индикатор хода выполнения 167; редактор обогащенного текста, создание 166; элементы ввода данных 157; UR L-aдpeca 161; адреса электронной почты 161; ввод числе 160; выбор файла 164; значение из списка 163; метки полей 158; номера телефонов 161; поисковые запросы 161 функции; хhг(), задание типа ответа 84; инкапсуляция 61 х холст 35; создание для игры "Пожиратели мозга" 329 ц цикл обновления 333 цикл рендеринга 334 ч чудо-кнопки 23 ш шаблоны; внешние, создание 1 1 7; декларативные 1 14; динамическая замена в элементе управления List View 224; императивные 1 12; и селекторы запросов 1 1 6 шаблоны проектов Visual Studio 40; приложения с разделением 44; приложения таблицы 40; с фиксированным макетом 44 э электронной почты адреса, ввод в НТМL-форме 161 элементы приложения Ма газина Wi ndows; CSS3 36; HTML 5 35; Ja vaScгipt 35; j Queгy 37; WinJ S 37 элементы управления; DatePickeг 134; Flip Vie\v 143; Htm!C oпtгol 279; ListVi ew. См. ListVi ew, элемент управления; декларативное создание 122; императивное создание 124; объявление 121 А Аj ах-запросы, выполнение с помощью функции xhr( ) 82 апу(), метод 75 AppB ar, элемент управления. См. панель приложения с change( ), метод, реализация 235 CO RS (W3C Cгoss-Oгigiп Resouгce Shaгing) 84 CSS 3 ( C ascading Style Sheets 3); адаптация к состоянию просмотра с помощью опроса носителя 272; как элемент приложения Магазина Wi пdows 36 D data-• 35 data-win-Ьind, атрибут 100 DatePic keг, элемент управления 134; показ части даты 137; получение выбранной даты 138; форматирование даты 135 Devices, чудо-кнопка 23 F FiddleI2, инструмент 243 File API ( H TML5) 35 FlexBox 36 Fl ipVie,v; элемент управления 143; отображение номеров страниц 146; создание нестандартных кнопок 148 Flyout, элемент управления 171 G getCouпt(), метод, реализация 232 н Htшl Сопtгоl; элемент управления 279 1 IListDa taAdapteг интерфейс, методы 231 IпdexedDB, база данных; выборка объектов из хранилища 250; диапазоны ключей 250; добавление объектов в хранилище 249; индексы 250; подключение к 248 i11seгtAt E11d() метод, реализация 234 i11tellise11se, файл 38 J Java Scгipt; как элемент приложения Магазина Wi пdO\vs 35; классы; наследование 65; определение 63; примеси, создание 67 joiп(), метод 76 j Queгy; Предметный указатель как элемент приложения Магазина Wi пdows; j Query 37 L ListVi ew, элемент управления 194; выбор элементов 205; нескольких 21 О; группировка 216; динамическая замена шаблона 224; переключение представлений с помощью контекстного масштабирования 219; постепенная загрузка 226; предотвращение перекрытия элементов 201; представления <�основной/ подробности» 207; сеточный макет 199; сортировка 213; списковый макет 199; фильтрация 214 Live Co1111ect API; аутентифика ция пользователей 304; вход; метод WL.logiп() 304; элемент управления Sig11I11 305; получение пользовательской информации 314; события аутентификации 307; установка Live SDK 299 Live SDK; добавление ссылки 299; инициализация 301; установка 299 м шediaMatcЬ( ), метод 27 4 Мепu, элемент управления; объявление 173; поддерживаемые команды 173; показ всплывающих элементов 175 Mi cгosoft SkyD гive; доступ 317; файлы; Предметный укзатель р закачивание 322; получение списка 317; скачивание 320 Pa geContro!Navigatщ элемент управления 293 patteгn, атрибут, контроль данных в НТМL-ф орме 152 Q QueгyCollection, класс 80 R Rat ing, элемент управления 131; настройка 132; отправка оценки 132 remove() метод, реализация 234 гequiгed, атрибут, контроль данных в НТМL-ф орме 152 s Seaгch, чудо-кнопка 23 setNotif icationHandleг( ) метод, реализация 236 Settings, чудо-кнопка 23
Shaгe, чудо-кнопка 23 Signlп, элемент управления, вход в Live Соппесt 305 Start, чудо-кнопка 23 т TimePick eг, элемент управления 139; установка текущего времени 141; форматирование 142 ToggleSwitch, элемент управления 129 Tooltip, элемент управления 127 v Viгtua lizedDataSource; класс 231 Vis ual Studio; выбор шаблона проекта 40; запуск приложения Магаз ина Wi пdows 47; консоль ] avaScript, отладка приложения 49; обозреватель модели DOM, отладка приложения 50; создание проекта приложения Магазина Wi пdows 27; тестирование состояния приложения 266
w \viпdow.setlпteгval( ), метод 7 4 Wi ndo\vs Azure Moblle Seгvices 247 х х!1г(), фун кция; выполнение Аjах-запросов 82; задание свойств объекта Xm1НttpRequest 85; задание типа ответа 84; параметры 84 Книги издательства «ДМК Пресс» можно заказать в торго во-издатель­
ском холдинге «А ЛЬЯI-1 С БУ КС» наложенным платежом, выслав открытку или письмо по почтовому адресу: 123242, Москва, а/я 20 или по электрон­
ному адресу: orders@aliaпs-kniga.ru. При оформлен ии заказа следует указать адрес (по лностью ), по кото­
рому долж ны быть высланы книги; фамилию, имя и отчество получа теля. Желател ьно также указать свой телефон и электро нный адрес. Эти книги вы можете заказать и в интернет-м агазине: www.alians-kпiga.ru. Оп товые закуп ки: тел. (499) 725-54-09, 725-50-27; Электро нный адрес books@aliaпs-kniga.ru. Штефен Ва льтер Разработка приложений для Windows 8 с помощью HTML5 и J avaScript Подробное руководст во Главный редактор Мовчаи Д. А. dm@dmk-press.ru Перевод с английского Слиикии А. А. Корректор Сиияева Г. И. Верстка Паранская Н. В. Дизайн обложки Мовчаи А. Г. Подписано в печать 1 5.03.201 3. Формат 60х90 1/16• Гарнитура «Петербург». Печать офсетная. Усл. печ. л. 2 1,07. Тираж 200 экз. заказ № WеЬ-сайт издательства: www.dmk-pгess.гu 
Автор
unnotigkeit
Документ
Категория
Без категории
Просмотров
1 352
Размер файла
23 651 Кб
Теги
win8js
1/--страниц
Пожаловаться на содержимое документа