Добро пожаловать в третью часть нашей серии по разработке WebOS SDK. Во второй части мы предоставили статические данные в список. Этот урок покажет вам, как загрузить динамические данные в список. Мы собираемся использовать AJAX и YQL для достижения этой цели.
Spinner и Scrim
Давайте начнем с изменения HTML списка сцены. Отредактируйте app / views / list / list-scene.html, чтобы он содержал следующее:
01
02
03
04
05
06
07
08
09
10
11
|
<div id=»myHeader» class=»tuts-header palm-page-header multi-line»>
<div id=»titleImage» class=»title-image»></div>
</div>
<div class=»palm-scrim» id=»search_divScrim» style=»display:none;»>
<div id=»search_divSpinner» x-mojo-element=»Spinner»></div>
</div>
<div id=»listWrapper» class=»palm-list main-list»>
<div id=»MyList» x-mojo-element=’List’></div>
</div>
|
Заголовок и список похожи на те, что в основной сцене. То, что мы добавляем, это холст и спиннер. Что такое выжимка и спиннер? Вращатель показывает вращающееся изображение (сюрприз!) Как признак того, что операция выполняется. Это хороший выбор для отображения счетчика для каждой операции, которая займет некоторое время (и помните, поскольку мы работаем на мобильном устройстве, операции, которые получают удаленные данные через беспроводное соединение, могут занять некоторое время). Кроме того, мы используем сетку (полупрозрачный слой, используемый для затемнения фонового пользовательского интерфейса), чтобы скрыть фоновый пользовательский интерфейс во время отображения счетчика, поскольку не имеет смысла взаимодействовать с приложением, пока выполняется операция.
Мы также добавили в наш список упаковочный элемент div, чтобы опустить его под заголовок. Определите необходимый класс в файле stylesheet / tutsplus.css для этого:
1
2
3
|
.main-list {
padding-top: 48px;
}
|
Затем перейдите в app / assistantants / list-assistant.js, чтобы добавить логику приложения. Сначала мы определяем модель списка. В отличие от прошлого раза, когда данные модели были статическими, наша модель списка не будет иметь никаких данных — она будет загружена в список позже.
1
2
3
4
5
6
7
8
|
this.myListModel = { items : [] };
this.myListAttr = {
itemTemplate: «list/itemTemplate»,
renderLimit: 10,
dividerTemplate: «list/dividerTemplate»,
dividerFunction : this.whatPosition
};
|
Разделитель списка
На этот раз мы определяем два новых свойства в наших атрибутах списка: dividerTemplate и dividerFunction. Позвольте мне сначала объяснить разделители. По сути, они являются элементами, помещаемыми между записями списка, для их группировки В нашем приложении мы хотим сгруппировать отображаемые статьи по дате. Идем дальше и создаем функцию divider:
1
2
3
4
5
|
ListAssistant.prototype.whatPosition = function(listitem){
var myDate = new Date(listitem.pubdate);
var ds=Mojo.Format.formatDate(myDate,{date:»long»,countryCode:»US»});
return ds;
}
|
Элемент list передается нашей функции, и мы создаем объект даты javascript из его свойства pubDate (это относится к дате публикации, которую мы получаем из RSS-канала). Затем мы переформатируем эту дату с помощью функции Mojo в длинную строку даты (например, 6 сентября 2010 г.) и возвращаем ее. Затем логика списка будет использовать эту дату для группировки элементов списка, имеющих одинаковую дату. DividerTemplate определяет, как выглядит фактический разделитель. Отредактируйте приложение / views / list / dividerTemplate.html:
1
2
3
4
5
6
7
8
9
|
<table class=»palm-divider labeled»>
<tr>
<td class=»left»></td>
<td class=»label»>
#{dividerLabel}
</td>
<td class=»right»></td>
</tr>
</table>
|
Каждый раз, когда список отображает разделитель, он вставляет указанный выше HTML-код в замену # {dividerLabel} на строку даты.
Давайте теперь создадим шаблон списка, отредактируем app / views / list / itemTemplate.html:
1
2
3
|
<div class=»palm-row grid-cell» x-mojo-tap-highlight=»immediate»>
<div><span class=»button #{clsname}»>#{category}
</div>
|
Опять же, мы указываем, как выстроена каждая строка списка и какие данные модели отображаются. Также добавьте новые классы в stylesheets / tutsplus.css:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
.pubdate {
font-size: 10px;
}
.creator {
font-size: 12px;
background-color: #a0a0a0;
float: right;
padding: 3px 3px;
text-align: right;
margin-right: 14px;
margin-top: 4px;
color: white;
}
.ellipsis {
padding: 10px 0px;
margin-left: 14px;
font-size: 19px;
width: 95%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.descr {
font-size: 14px;
margin-left: 14px;
width: 95%;
}
.button {
width: 95%;
overflow: hidden;
white-space: nowrap;
text-overflow: clip;
margin-left: 14px;
padding: 3px 3px;
-webkit-border-radius: 8px;
color: white;
font-size: 14px;
text-decoration: none;
vertical-align: middle;
}
.Nettuts {
border-top: 1px solid #4a9082;
background: #2e6a60;
}
.Vectortuts {
background: #19487e;
}
.Psdtuts {
background: #a51500;
}
.Activetuts {
background: #a5290a;
}
.Aetuts {
background: #4a3a57;
}
.Cgtuts {
background: #73434f;
}
.Phototuts {
border-top: 1px solid #3297b5;
background: #2e92b2;
}
.Audiotuts {
background: #3d6b00
}
.Mobiletuts {
border-top: 1px solid #ffd200;
background: #d19c00;
}
|
В конце функции настройки добавьте последний отсутствующий фрагмент, настройку счетчика:
1
2
|
this.controller.setupWidget(«search_divSpinner», { spinnerSize : «large» }, { spinning: true } );
}
|
Обратите внимание, что мы уже установили вращение, но поскольку DIV, содержащий вращатель, скрыт, мы не увидим изображение вращателя.
Хорошо, давайте перейдем к редактированию функции активации:
1
2
3
4
5
6
7
8
|
ListAssistant.prototype.activate = function(event) {
/* put in event handlers here that should only be in effect when this scene is active.
example, key handlers that are observing the document */
this.headerTitleElement.innerHTML=»<img src=»+this.titleimage+»>»
this.getData();
}
|
AJAX и YQL
Мы отображаем title-image и вызов getData. Это загрузит данные, которые мы хотим отобразить в нашем списке. Давайте добавим функцию getData:
1
2
|
ListAssistant.prototype.getData = function() {
$(«search_divScrim»).show();
|
Прежде чем получить данные, мы покажем DIV, содержащий счетчик. Мы покажем счетчик, пока выполняется операция загрузки. Наша цель — отобразить в нашем списке последние сообщения с выбранного сайта tutsplus. Каждый сайт tutsplus экспортирует свои последние статьи в RSS-канал. Как мы читаем RSS-канал для использования в нашем приложении? Мы собираемся использовать YQL, Yahoo! Язык запросов — это выразительный SQL-подобный язык, который позволяет запрашивать, фильтровать и объединять данные через веб-службы (http://developer.yahoo.com/yql/) Я не буду вдаваться в подробности о YQL здесь, вы можете узнать больше об этом в Nettuts .
Вот как мы получаем данные из mobiletuts с помощью YQL:
1
|
select * from rss where url=’http://feeds.feedburner.com/mobile-tuts-summary’
|
Воспользуйтесь консолью YQL по адресу http://developer.yahoo.com/yql/console, чтобы попробовать ее. Выберите JSON в качестве выходного формата. Вот (сокращенный) результат:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
{
«query»: {
«count»: «1»,
«created»: «2010-09-07T08:41:32Z»,
«lang»: «en-US»,
«results»: {
«item»: [
{
«title»: «Introduction to webOS SDK Development: Part 2»,
«link»: «https://mobile.tutsplus.com/tutorials/webos/introduction-to-webos-sdk-development-part-2/»,
«comments»: «https://mobile.tutsplus.com/tutorials/webos/introduction-to-webos-sdk-development-part-2/#comments»,
«pubDate»: «Mon, 30 Aug 2010 12:00:40 +0000»,
«creator»: «Markus Leutwyler»,
«category»: [
«webOS»,
«webOS internet»,
«webOS rss»,
«webOS SDK»,
«webOS table view»
],
«guid»: {
«isPermaLink»: «false»,
«content»: «http://mobile.tutsplus.com/?p=2392»
}
]
}
}
});
|
Похоже, мы можем использовать большую часть этих данных для отображения в нашем списке. Вы спрашиваете, как мы попали в наш список? AJAX это ответ. Мы будем использовать AJAX-запрос для вызова веб-службы YQL. Так как mobiletuts использует другой канал, чем другие сайты, нам нужно изменить URL канала вручную.
1
2
3
4
5
6
|
var feed=this.title.toLowerCase();
if (feed==’mobiletuts’) {
feed=’mobile-tuts-summary’;
} else {
feed=feed+’-summary’;
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
var query=»Select * from rss where url=’http://feeds.feedburner.com/»+feed+»‘»;
var url = «http://query.yahooapis.com/v1/public/yql?q=»+encodeURIComponent(query)+»&format=json»;
var request = new Ajax.Request(url, {
method: ‘get’,
asynchronous: true,
evalJSON: «false»,
onSuccess: this.parseResult.bind(this),
on0: function (ajaxResponse) {
// connection failed, typically because the server is overloaded or has gone down since the page loaded
Mojo.Log.error(«Connection failed»);
},
onFailure: function(response) {
// Request failed (404, that sort of thing)
Mojo.Log.error(«Request failed»);
},
onException: function(request, ex) {
// An exception was thrown
Mojo.Log.error(«Exception»);
},
});
}
|
Мы используем функцию Prototype Ajax.Request для вызова веб-службы Yahoo. Поскольку вызовы AJAX являются асинхронными, мы не знаем, когда вернем данные из веб-службы. Мы указываем функцию для вызова при получении данных в обратном вызове onSuccess: this.parseResult.bind (this)
В вызове обратного вызова есть что-то новое, обратите внимание на добавленный оператор .bind (this). Позвольте мне объяснить, что означает «это» и область действия в JavaScript: в JavaScript функции выполняются в определенном контексте, называемом «область действия». Внутри функции ключевое слово this становится ссылкой на эту область. Например, переменная this.title, которую мы используем в функции getData, является локальной для этой функции и не будет доступна в другой функции. Введите .bind (это). «Связывание» в основном определяет значение ключевого слова this при запуске функции. В нашем примере, когда мы вызываем this.parseResult.bind (this), его переменные, на которые они ссылаются, доступны в функции parseResult
Данные, возвращаемые из вызова веб-службы, попадают в транспортный объект, который передается в функцию parseResult. Нас интересует текстовое свойство transport.reponse, которое содержит выходные данные в виде строки JSON. Мы конвертируем это в объект, вызывая evalJSON. Затем мы можем просмотреть свойства данных JSON и собрать данные, которые мы хотим заполнить, в наш список.
Разбор JSON
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
ListAssistant.prototype.parseResult = function(transport) {
var newData = [];
var data=transport.responseText;
try {
var json = data.evalJSON();
}
catch(e) {
Mojo.Log.error(e);
}
k=0;
for (j=0;j<json.query.count;j++) {
var cat=»»;
var categories=json.query.results.item[j].category;
for (i=0;i<categories.length;i++) {
if (i==0) { cat=categories[i];
}
var descr=json.query.results.item[j].description;
var ipos=descr.indexOf(‘[…]’);
if (ipos==-1) { ipos=descr.indexOf(‘…’);
if (ipos!=-1) { descr=descr.substr(0,ipos);
descr=descr+’…’
newData[k] = {data: json.query.results.item[j].title, guid: json.query.results.item[j].guid.content, category: cat, pubdate: json.query.results.item[j].pubDate, creator: json.query.results.item[j].creator, clsname: this.title, description: descr };
k++;
}
|
Поскольку категории для каждой статьи являются динамическими, мы просто извлекаем первые 3 категории из данных JSON и создаем из них новую строку категорий (с именем cat). Нам также нужно сократить описание, потому что канал иногда содержит HTML-строки, которые мы не хотим отображать. Хорошо, мы проанализировали наш ответ JSON и создали из него новый массив. Этот массив является базой для нашей модели списка.
1
2
3
4
5
6
7
|
this.myListModel[«items»] = newData;
this.controller.modelChanged(this.myListModel , this);
// hide the spinner
$(«search_divScrim»).hide();
};
|
Сначала мы передаем массив newData элементам нашей модели списка, а затем уведомляем список о том, что есть новая модель, с которой можно работать. Затем список будет отображать список с новыми данными. Наконец, мы скрываем наш счетчик, чтобы показать пользователю, что процесс загрузки завершен.
Упакуйте приложение, установите и запустите его. Для каждого выбранного вами сайта tutsplus вы должны увидеть список, заполненный последними статьями.
Заворачивать
Поздравляем! Мы прочитали содержимое RSS через YQL и поместили эти данные в наш список. В четвертой части мы собираемся добавить последний недостающий фрагмент в наше приложение!