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

Поиск и общий доступ

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

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

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

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

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

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

Общий доступ (Share)

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

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

  • Создать обработчик события запроса информации для общего доступа (datarequested).
  • Зарегистрировать это событие в DataTransferManager в нужном контексте (и не забыть отвязать обработчик при выходе из контекста).

Начнем с первой задачи. Для этого откройте файл data.js, в нем мы опишем общий обработчик для передачи информации в общий доступ.

Добавьте в файл следующую функцию, возвращающую обработчик события datarequested:

function getSharingDataRequestedHandler(item) {
    var handler = function onSharingDataRequested(event) {
        var request = event.request;
        
        var request = event.request;
        var text = item.title;
        if (item.link) {
            text += "\n\r" + item.link;
            request.data.setUri(new Windows.Foundation.Uri(item.link));
        }
        request.data.properties.title = text;
        // Описание
        // request.data.properties.description = item.description;
        request.data.setText(text);

        };

    return handler;        
}

Здесь ключевым моментом является свойство request внутри аргументов события (event), которое содержит объект DataRequest (http://msdn.microsoft.com/ru-ru/library/windows/apps/br205924.aspx). В поле data этого объекта в свою очередь находится объект DataPackage (http://msdn.microsoft.com/ru-ru/library/windows/apps/windows.applicationmodel.datatransfer.datapackage.aspx), в который собственно и прописываются необходимые свойства.

Замечание: если создание объекта DataPackage занимает какое-то время (например, внутри используются асинхронные операции), рекомендуется использовать метод getDeferral (http://msdn.microsoft.com/ru-ru/library/windows/apps/windows.applicationmodel.datatransfer.datarequest.getdeferral.aspx).

Обратите также внимание на то, что для передачи данных (например, текстового значения) используются поля объекта item, который доступен внутри обработчика через замыкание. Также его можно передать через связывание функции (bind).

Упражнение. Попробуйте самостоятельно добавить в передаваемые параметры описание объекта (request.data.properties.description), определив, соответствующе значение для item (например, его можно сгенерировать на основании контента элемента, взяв только текстовое содержимое и ограничив длину текста).

Теперь давайте зарегистрируем эту функцию в объекте Data, который используется глобально в нашем проекте. Для этого найдите определение объекта Data:

WinJS.Namespace.define("Data", {
	…
});

Добавьте в него ссылку на описанную выше функцию:

WinJS.Namespace.define("Data", {
    …
    getSharingHandler: getSharingDataRequestedHandler
});

Следующим шагом нужно зарегистрировать наш обработчик в правильном контексте. Для нас таким контекстом будет страница отдельного элемента (itemDetail). Откройте соответствующий js-файл (itemDetail.js).

Добавьте внутри функции ready в самом конце вызов:

ready: function (element, options) {

    this.initContracts(item);
}

После функции ready добавьте новую функцию unload, которая будет вызвана при уходе с этой страницы (нам нужно убрать обработчик события):

 unload: function () {
    this.clearContracts();
}

Теперь давайте опишем соответствующие функции. После функции unload опишите функции initContracts и clearContracts:

sharingHandler: null, 

initContracts: function(item) {
    var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
    this.sharingHandler = Data.getSharingHandler(item);
    dataTransferManager.addEventListener("datarequested", this.sharingHandler);
},

clearContracts: function () {
    var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView();
    if (this.sharingHandler) {
        dataTransferManager.removeEventListener("datarequested", this.sharingHandler);
    }
}

Теперь попробуйте запустить приложение, перейти к конкретному элементу и открыть общий доступ:


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


Поиск

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

Первым делом необходимо добавить в проект новый элемент – контракт поиска:


Для удобства, сгенерированную страницу вместе со стилями и js-файлом мы разместим внутри папки pages/search.

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

Откройте страницу default.html и добавьте наверху ссылку на поисковый скрипт:

<script src="/pages/search/searchResults.js"></script>

В этом скриптовом файле описывается, как должна инициализироваться страница поиска и, что не менее важно, описывается, как приложение должно себя вести при активации через поисковый запрос:

WinJS.Application.addEventListener("activated", function (args) {
    if (args.detail.kind === appModel.Activation.ActivationKind.search) {
        args.setPromise(ui.processAll().then(function () {
            if (!nav.location) {
                nav.history.current = { location: Application.navigator.home, initialState: {} };
            }

            return nav.navigate(searchPageURI, { queryText: args.detail.queryText });
        }));
    }

Перейдите к файлу searchResults.js и найдите функцию _searchData:

_searchData: function (queryText) {
    var originalResults;
    // TODO: Perform the appropriate search on your data.
    if (window.Data) {
        originalResults = Data.items.createFiltered(function (item) {
            return (item.title.indexOf(queryText) >= 0 || item.subtitle.indexOf(queryText) >= 0 
            || item.description.indexOf(queryText) >= 0);
        });
    } else {
        originalResults = new WinJS.Binding.List();
    }
    return originalResults;
}

Убедитесь, что в области поиска у вас используются существующие (доступные) поля элемента item. Например, если вы следовали предыдущим лабораторным работам, вполне вероятно, что у вас не будет поля subtitle, зато есть поле content. Возможно, у вас также нет поля description.

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

return (item.title.indexOf(queryText) >= 0 || item.subtitle.indexOf(queryText) >= 0
 || item.description.indexOf(queryText) >= 0);

Например, вот так:

return (item.title.indexOf(queryText) >= 0 || item.content.indexOf(queryText) >= 
 0 || item.description.indexOf(queryText) >= 0);

Далее откройте файл searchResults.html. Найдите шаблон itemtemplate и внутри него строчки:

<div class="item-content">
    <h3 class="item-title win-type-x-small win-type-ellipsis" 
    data-win-bind="innerHTML: title searchResults.markText"></h3>
    <h4 class="item-subtitle win-type-x-small win-type-ellipsis"
     data-win-bind="innerHTML: subtitle"></h4>
    <h4 class="item-description win-type-x-small win-type-ellipsis" 
    data-win-bind="innerHTML: description searchResults.markText"></h4>
</div>

Убедитесь, что в связывании указаны доступные вам поля. Например, если у вас в item нет поля subtitle, его надо удалить или заменить на что-либо еще:

<h4 class="item-subtitle win-type-x-small win-type-ellipsis"><span data-win-bind="innerHTML: month">
</span> <span data-win-bind="innerHTML: day"></span></h4>

Теперь если вы запустите приложение и попробуете что-нибудь поискать, вам откроется только что добавленная страница:

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

Перейдите к файлу searchResults.js, найдите функцию _initializeLayout. Внизу для не snap-режима в коде прописан заголовок приложения и фраза отображения поискового запроса:

 // TODO: Change "App Name" to the name of your app.
 document.querySelector(".titlearea .pagetitle").textContent = "App Name";
 document.querySelector(".titlearea .pagesubtitle").textContent =
    "Results for "" + this._lastSearch + '"';

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

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

 // TODO: Replace or remove example filters.
 this._filters.push({ results: null, text: "Group 1", predicate: function (item) 
 { return item.group.key === "group1"; } });
 this._filters.push({ results: null, text: "Group 2+", predicate: function (item) 
 { return item.group.key !== "group1"; } });

Замените этот код на следующий:

 var groups = Data.groups;

 for (var i = 0; i < groups.length; i++) {
     var group = groups.getAt(i);

     var predicate = (function (item) { return item.group.key === this.key; }).bind({ key: group.key });

     this._filters.push({results: null, text: group.title, predicate: predicate});
 }

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

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

Найдите функцию _itemInvoked, в ней, собственно, и нужно прописать, куда и как нужно перейти:

_itemInvoked: function (args) {
    args.detail.itemPromise.done(function itemInvoked(item) {
        // TODO: Navigate to the item that was invoked.
    });
},

Добавьте внутри следующий вызов:

WinJS.Navigation.navigate("/pages/itemDetail/itemDetail.html", 
{ item: Data.getItemReference(item.data) });

Запустите, проверьте, что все работает.

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

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

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

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

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