Microsoft — мировой лидер в области ПО и ИТ-услуг
513,66
рейтинг
21 декабря 2012 в 10:31

Разное → Введение в разработку WinRT-приложений на HTML/JavaScript. От шаблона к приложению с данными tutorial

Этой статьей мы открываем серию материалов, посвященных азам разработки WinRT-приложений на HTML/JS для Windows 8. Мы последовательно пройдем путь от стартового практически пустого шаблона к полноценному приложению с серверной частью и живыми плитками.

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

Создание приложения из шаблона


Откройте Visual Studio 2012, выберите создание нового проекта (File -> New -> Project…). Далее в шаблонах выберите проект на JavaScript -> Windows Store. Укажите, что будете использовать шаблон Grid App.



Укажите любое название проекта, например, Reader App.

Изучите структуру проекта:
  1. package.appxmanifest — манифест приложения, описывающий ключевые настройки, используемые возможности, название приложения, плитки и другие параметры;
  2. default.html — формальная стартовая страница приложения;
  3. pages\groupedItems — папка с html-, js- и css-файлами для страницы представления групп контента (подгружается в стартовую страницу);
  4. pages\groupDetail — папка с html-, js- и css-файлами для страницы отображения группы новостей (записей в rss-потоке), соответствующих одному потоку;
  5. pages\itemDetail — папка с html-, js- и css-файлами для страницы отображения каждой из новостей отдельно;
  6. js\data.js — js-файл, описывающий работу с данными (содержит зашитые внутрь демонстрационные данные);
  7. js\default.js — описывает события, необходимые для инициализации приложения;
  8. js\navigator.js — описывает логику переходов между страницами и необходимые для этого события и объекты;
  9. images\logo.png — изображение, используемое для квадратной плитки;
  10. images\smalllogo.png — изображение, используемое при перечислении приложения в операционной системе, например, при поиске или выборе приложений для поиска или общего доступа;
  11. images\splashscreen.png — загрузочное изображение, показываемое при открытии приложения;
  12. images\storelogo.png — изображение, используемое в интерфейсе магазина приложений (Windows Store).


Также по умолчанию к проекту подключена библиотека WinJS, содержащая наборы стилей для темной и светлой тем и вспомогательных функций и объектов на JavaScript.

Попробуйте запустить приложение, нажав F5, зеленую стрелочку или выбрав Debug -> Start Debugging.



Изучите работу приложения:
  • Попробуйте нажать на отдельную серую плитку или на заголовок группы.
  • Попробуйте нажать на кнопку назад во внутренних страницах.
  • Попробуйте перевести приложение в Snapped-режим.


Вернитесь в Visual Studio и остановите отладку (Shift+F5, красный квадратик или выберите в меню Debug -> Stop Debugging).

Замена источников данных


Откройте файл js\data.js. В нем объявлено несколько важных функций и объектов, которые мы также будет использовать.

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

  1. Вставка данных в список:

        // You can add data from asynchronous sources whenever it becomes available.
        generateSampleData().forEach(function (item) {
            list.push(item);
        });
    


  2. Генерация примеров данных:
        // Returns an array of sample data that can be added to the application's
        // data list. 
        function generateSampleData() {
            var itemContent = "<p>Curabitur class … ";
            var itemDescription = "Item Description: Pellente…";
            var groupDescription = "Group Description: Lorem …";
            …
            return sampleItems;
        }


Если вы запустите приложение, оно продолжит работать, только в нем будут отсутствовать какие-либо данные. Остановите отладку и вернитесь к файлу data.js. Давайте вкратце пройдемся по его структуре, чтобы были понятны дальнейшие действия:

var list = new WinJS.Binding.List();


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

var groupedItems = list.createGrouped(
        function groupKeySelector(item) { return item.group.key; },
        function groupDataSelector(item) { return item.group; }
    );

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

WinJS.Namespace.define("Data", {
        items: groupedItems,
        groups: groupedItems.groups,
        getItemReference: getItemReference,
        getItemsFromGroup: getItemsFromGroup,
        resolveGroupReference: resolveGroupReference,
        resolveItemReference: resolveItemReference
    });


Через функцию define в библиотеке WinJS (Namespace) прописывается глобальный объект Data, который будет доступен из других частей программы для работы с нашими данными. Внутри объекта прописываются ссылки на группированную коллекцию, список групп внутри нее и ряд функций, описанных в файле data.js и используемых для работы с коллекцией и извлечения данных.

Оставшиеся 4 функции (getItemReference, getItemsFromGroup, resolveGroupReference и resolveItemReference) используются для сравнения объектов, извлечения подмножеств элементов, принадлежащих одной группе, определения группы по ключу и элемента по набору уникальных идентификаторов.

Теперь самое время приступить к добавлению наших собственных данных. В качестве источников мы будет использовать внешние RSS-потоки.

Важно: в рамках данной статьи в целях упрощения кода для получения данных мы будет использовать только RSS-потоки, однако, добавление поддержки Atom-потоков не должно составить труда, так как они имеют схожую структуру и ключевая разница будет в адресации искомых полей данных.


Вернитесь к началу файла и после строчки «use strict» опишите блоги, информацию из которых вы будете выводить:

        var blogs = [
            {
                key: "ABlogging",
                url: "http://blogs.windows.com/windows/b/bloggingwindows/rss.aspx",
                title: 'Blogging Windows', rsstitle: 'tbd', updated: 'tbd',
                dataPromise: null
            },
            {
                key: "BExperience",
                url: 'http://blogs.windows.com/windows/b/windowsexperience/rss.aspx',
                title: 'Windows Experience', rsstitle: 'tbd', updated: 'tbd',
                dataPromise: null
            },
            {
                key: "CExtreme",
                url: 'http://blogs.windows.com/windows/b/extremewindows/rss.aspx',
                title: 'Extreme Windows', rsstitle: 'tbd', updated: 'tbd',
                dataPromise: null
            }];


Для описания каждого блога (группы контента) мы указываем:
  • ключ — key (так как сортировка групп будет по ключу, мы также добавили в начале группы латинские буквы для явного задания сортировки — в реальном проекте это можно сделать более элегантным способом),
  • ссылку на RSS-поток — url,
  • название потока — title,
  • несколько заглушек:
    • реальное название блога — rsstitle,
    • дата обновления (updated),
    • указатель на dataPromise — «обещание» загрузить этот поток и его обработать.



Чтобы превратить ссылки в данные на компьютере, информацию по ним необходимо загрузить. Для этого после строчки var list = new WinJS.Binding.List(); добавьте новую функцию getBlogPosts, которая как раз будет заниматься загрузкой:

function getBlogPosts(postsList) {
        blogs.forEach(function (feed) {
            // Создание Promise
            feed.dataPromise = WinJS.xhr( { url: feed.url } ).then(
                function (response) {
                    if (response) {
                        var syndicationXML = response.responseXML || (new DOMParser()).parseFromString(response.responseText, "text/xml");
                        processRSSFeed(syndicationXML, feed, postsList);
                    }
                }
            );
        });

        return postsList;
    }


В данной функции мы в цикле проходимся по всем блогам, для каждой ссылки через функцию WinJS.xhr создаем асинхронную Promise-обертку вокруг XMLHttpRequest-запроса и после получения результата (then) передаем полученный ответ на обработку в функцию processRSSFeed, которую мы опишем ниже.

Замечание: для используемых нами блогов нет необходимости в дополнительной проверке, что мы получили ответ в виде XML (наличие responseXML), однако, в общем случае это неверно: некоторые блоги из-за неверных настроек сервера/движка отдают RSS-поток как текстовое содержимое, которое необходимо дополнительно обрабатывать, если мы хотим работать с ним как с XML-файлом.


Для обработки потока добавьте нижу еще одну функцию — processRSSFeed:

    function processRSSFeed(articleSyndication, feed, postsList) {
        // Название блога
        feed.rsstitle = articleSyndication.querySelector("rss > channel > title").textContent;
        // Используем дату публикации последнего поста как дату обновления
        var published = articleSyndication.querySelector("rss > channel > item > pubDate").textContent;

        // Преобразуем дату в удобрый формат
        var date = new Date(published);
        var dateFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter(
           "day month.abbreviated year.full");
        var blogDate = dateFmt.format(date);
        feed.updated = "Обновление: " + blogDate;

        // Обработка постов
        getItemsFromRSSFeed(articleSyndication, feed, postsList);
    }


В данной функции мы пользуемся тем фактом, что на входе имеем XML-файл с известной структурой (RSS 2.0), по которому можно перемещаться используя DOM-модель, в частности, функцию querySelector, используя которую, можно вытаскивать из полученного документа необходимые данные.

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

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

Добавьте ниже следующую функцию:

function getItemsFromRSSFeed(articleSyndication, feed, postsList) {
        var posts = articleSyndication.querySelectorAll("item");

        // Цикл по каждому посту в потоке
        var length = posts.length;
        for (var postIndex = 0; postIndex < length; postIndex++) {
            var post = posts[postIndex];
            // форматирование даты
            var postPublished = post.querySelector("pubDate").textContent;
            var postDate = new Date(postPublished);
            var monthFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("month.abbreviated");
            var dayFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("day");
            var yearFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("year.full");
            var timeFmt = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter("shorttime");
            
            var postContent = toStaticHTML(post.querySelector("description").textContent);
            
            var postItem = {
                index: postIndex,
                group: feed,
                // заголовок поста
                title: post.querySelector("title").textContent,
                // дата и отдельные компоненты
                postDate: postDate,
                month: monthFmt.format(postDate).toUpperCase(),
                day: dayFmt.format(postDate),
                year: yearFmt.format(postDate),
                time: timeFmt.format(postDate),
                // содержимое поста
                content: postContent,
                // ссылка на пост
                link: post.querySelector("link").textContent
            };

            postsList.push(postItem);
        }
    }


В данной функции мы проходимся в цикле по всем постам в полученном потоке, выбирая из XML-описания нужные поля (заголовок, дату публикации, контент и т.п.), после чего собираем нужную информацию в один объект (postItem), который добавляем в список постов.

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

Замечание: в общем случае работы с RSS-потоками из неконтролируемых источников нужно внимательно контролировать (и исследовать) получаемые данные. На практике часть данных по искомым полям может отсутствовать, поэтому прежде, чем извлекать текстовое содержимое (textContent) необходимо убеждаться, что предыдущая операция вернула не null-значение. В некоторых случаях полное содержимое поста может скрываться за элементами encoded или full-text или и вовсе отсутствовать. Также известны случаи, когда сервер отдает дату в неправильном формате, в результате чего стандартный парсер выдает исключение.


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

list = getBlogPosts(list);


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


Как видите, оно уже использует наши новые данные, однако, есть несколько «проблемных» зон, которые необходимо поправить:

  • нет картинок,
  • в отдельных строчках выводится странное «undefined»,
  • на главной странице выводится сразу весь список из потока, а хочется его ограничить несколькими записями.


Этими задачами мы займемся в следующих статьях.

Продолжение


Автор: @kichik
Microsoft
рейтинг 513,66
Microsoft — мировой лидер в области ПО и ИТ-услуг

Комментарии (12)

  • +4
    Как-то глупо получилось, что статья оборвана посередине.

    Бывает, я заснуть не могу, пока у меня в приложении «нет картинок,
    в отдельных строчках выводится странное „undefined“»
  • –8
    Далее в шаблонах выберите проект на JavaScript -> Windows Store. Затем нажмите ALT+F4!!!
    • НЛО прилетело и опубликовало эту надпись здесь
      • –5
        Вполне себе нормальная шутка. Не надо так жестко реагировать
        • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Опять RSS.
    • 0
      Ну а что еще?! (Риторический вопрос) Если сами MS в своем блоге изжевали тему RSS ридера вдоль и поперек, не знаю правда это на базе их статьи или нет, уже не вспомню, а вспоминать и не хочется.
    • +1
      Не переживайте, примерно к 6-7 статье станет понятно, что одним RSS для хорошего приложения не отделаешься ;)
    • +1
      А что в tutorial разбирать? Я в своё время долго выбирал, что может покрыть максимальное количество сценариев и получается, что RSS — это оптимально:
      — получение данных их интенета
      — разбор данных и их привязка к контролам
      — красивое отображение данных
      — понятно для чего реализуемые контракты поиска и share
      — понятно, что должно быть на плитках
      — понятно, что должен делать сервис Push Notification (если его делать)
      — финально — понятно что выносить в настройки

      Получается, хорошая тема для рассказа о том, как делать типовые для WinRT приложения вещи.
  • 0
    Вот забавно, как только я перешел на Мак, так у винды появилась приличная IDE для работы с JS.
  • 0
    а как насчет database? перерыл вс епримеры в MSDN и не могу понять что родного идет внутри для JS? localStore вроде есть, но это чисто аналог Dictionary для храненния переменных. А вот аналог близкий к SQLite уже какието кастомные костыли. То ктото конвертит JSON в строку и оьратно и хранит строкой в localStore, чтото пишет либы для перегона Dictionary в Json и обратно для хранения. И все примеры что я видел, аналогичны RSS без offline данных. Неужели дл япростых вещей мне нужно подключать левые либы или писать тучу своих методов? Или я так понимаю все сводится к тому, что JS Только для примитивов, а чтото большее уже только на C#?
    • 0
      IndexedDB

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

Самое читаемое Разное