Tym razem z poziomu WinJS dokonałem integracji z systemową wyszukiwarką, sprawiając tym samym osiągnięcie przez aplikację WinJS tej samej funkcjonalności, którą wcześniej zrealizowałem w C#. Oczywiście wiązały się z tym też mniejsze zadania…
Kontrakt Search oraz stronę z wynikami wyszukiwań najszybciej dodamy korzystając z Visual Studio 2012. Z menu kontekstowego w oknie Solution Explorer wybieramy Add New Item, a następnie szablon Search Contract. Dokumentacja opisuje wszystkie potrzebne czynności oraz co zostaje dodawane do projektu aplikacji po wybraniu szablonu. Chwilę może zająć analiza kodu wygenerowanej strony. Parę rzeczy zwróciło w praktyce moją uwagę. Code-behind strony z wynikami wyszukiwań jest zadeklarowany w default.html, zamiast w HTML tej strony. Jest to podyktowane tym, że:
- obsłużenie zdarzenia aktywacji aplikacji przez wyszukiwarkę
- rejestracja na wpisanie zapytania w wyszukiwarce
muszą odbywać się globalnie przy uruchomieniu aplikacji i nie zdecydowano się na modyfikację pliku z kodem globalnym. Konkretnie chodzi o kod:
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 });
}));
}
});
appModel.Search.SearchPane.getForCurrentView().onquerysubmitted = function (args) { nav.navigate(searchPageURI, args); };
Jednocześnie HTML strony z wynikami wyszukiwania nie odwołuje się do pliku JavaScript o takiej samej nazwie, co powoduje, że strona w designerze Blenda nie prezentuje się w prawidłowy sposób. Jeśli jednak otworzymy default.html i w trybie interaktywnym przenawigujemy do strony z wynikami, to prezentuje się ona wtedy prawidłowo. Stylowanie jest wtedy przyjemnością….
W wygenerowanej stronie są przykładowe dane, które potrzebujemy z reguły zmienić. W moim przypadku potrzebuję filtrować pobrane pliki wideo, które są zapisywane w folderze lokalnym aplikacji. W definicji strony powstał w związku z tym następujący kod:
ready: function (element, options) {
…
this._handleQuery(element, options);
…
},
_handleQuery: function (element, args) {
…
this._searchData(element, this._queryText);
…
},
_searchData: function (element, queryText) {
if (window.Data) {
var folder = Windows.Storage.ApplicationData.current.localFolder;
var queryOptions = new Windows.Storage.Search.QueryOptions(Windows.Storage.Search.CommonFileQuery.defaultQuery, [".mp4"]);
queryOptions.folderDepth = Windows.Storage.Search.FolderDepth.deep;
queryOptions.indexerOption = Windows.Storage.Search.IndexerOption.useIndexerWhenAvailable;
if (queryText !== "")
queryOptions.userSearchFilter = queryText;
var fileQuery = folder.createFileQueryWithOptions(queryOptions);
var dataSourceOptions = {
mode: Windows.Storage.FileProperties.ThumbnailMode.picturesView,
requestedThumbnailSize: 95,
thumbnailOptions: Windows.Storage.FileProperties.ThumbnailOptions.useCurrentScale
};
var dataSource = new WinJS.UI.StorageDataSource(fileQuery, dataSourceOptions);
var listView = element.querySelector(".resultslist").winControl;
listView.itemDataSource = dataSource;
}
}
By cieszyć się widokiem:
dokonanałem jeszcze trochę zmian w kodzie i stylach strony. Na łamach krótkiego postu nie wydają się one aż tak istotne, by je omawiać, z wyjątkiem kolorowania wyszukiwanej frazy na elementach ListView.
Wygenerowana strona otrzymuje już taką funkcjonalność w postaci zdefiniowanej funkcji konwertera _markText. W czym więc problem? Pułapka tkwi w klasie StorageDataSource. Jak już ostatnio w swoich postach pisałem, wymaga ona bindingów onetime. Jak zdefiniować w WinJS binding onetime, który korzysta z konwertera? Nie jest to tak oczywiste jak… w XAML-u. Sprawę zakończyło zdefiniowanie nowego bindingu zawierającego w sobie także logikę konwertera:
_bindMarkedText: function (source, sourceProperty, destination, destinationProperty) {
var element = destination;
var text = source && source[sourceProperty];
if (!text) return;
if (this._lastSearch)
destination[destinationProperty] = text.replace(this._lastSearch, "<mark>" + this._lastSearch + "</mark>");
else
destination[destinationProperty] = text;
},
Większa powszechność tworzenia własnych bindingów i ich większa otwartość jest typowa także dla innych bibliotek JS (np. Knockout’a). W ich logice oprócz konwersji mogą znajdować się także operacje na elementach wizualnych, w tym animacje. Wracając do naszego przypadku chciałbym zwrócić uwagę na funkcję WinJS.Binding.initializer, której użyłem do zadeklarowania, że funkcja _bindMarkedText może być stosowana w deklaratywnym data bindingu.
_handleQuery: function (element, args) {
…
WinJS.Namespace.define("searchVideos", { bindMarkedText: WinJS.Binding.initializer(this._bindMarkedText.bind(this)) });
…
},
Teraz wystarczy dopilnować, by w HTML tekst w szablonie listy był zbindowany tak:
<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
<div class="item">
…
<div class="item-content">
<h3 class="item-title win-type-x-small" data-win-bind="innerHTML: name searchVideos.bindMarkedText"></h3>
</div>
</div>
</div>
Na koniec zmieniłem sobie w CSS kolor zaznaczonego tekstu z domyślnego niebieskiego na pomarańczowy:
.searchVideos section[role=main] .resultslist .item .item-content mark {
background: transparent;
color: #D8512B;
}
Do usłyszenia.
Brak komentarzy:
Prześlij komentarz