上一篇, 开发一个移动应用程序,我们构建了通用布局模板和搭建了一个静态应用。本篇将静态内容更新为从Flickr动态获取数据和展示feeds.你会学到从JSON获取响应内容和处理异常,使用进度表表示在等待服务器的返回,动态填充队列,使用基本HTML模板创建队列和将数据格式化为特定的语言环境。准备好了吗?我们开始!
应用数据结构
我们要组织JSON请求结构,并通过JSON返回更新页面,我们在HTML文件声明一个合局范围的基本数据结构。该对象要求:
- 包含查询参数的JSON请求,用于Feed view
- 根据用户在Settings view的输入进行更新
require([ //... ], function (parser) { flickrview = {}; flickrview.QUERY = { tags: "famous,bridges", tagmode: "all", format: "json", lang: "en-us" }; //... });
这就定义完成,任务组件都可以set和get flickrview.QUERY.
FeedView属性
Feed view负责获取和显示最新上传到Flickr上的照片,我们需要一个url和一些参数通过JSONP请求来取得Flickr公共服务:
// Flickr public feed URL to pull recent photo uploads from requestUrl: "http://api.flickr.com/services/feeds/photos_public.gne", // JSONP request options and query parameters requestOptions: { jsonp: "jsoncallback", preventCache: true, timeout: 10000, query: null },
请求参数在调用之前将动态设置,更多的Dojo请求参数,参见Dojo request Reference Guide.
参见静态页面,你会发现照片列表需要特别格式化以包括标题、发布时间和任务信息。我们来创建一个适应照片feed模板的属性:
// Create a template string for a photo ListItem flickrviewItemTemplateString: '' + '' + '',${title}' + '${published}' + ' ' + '
我们也定义一个方法替代模板变量:
substitute: function(template,obj) { return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, function(match,key){ return obj[key]; }); }
继续小部件的引用,初始化工作将在开始方法中完成
//... refreshButton: null, feedList: null, feedHeading: null, progressIndicator: null, detailsContainer:null, detailsHeading:null, //...
FeedView启动
在解析和创建的子部件都完成之后,Dojo小部件启动任务被调用一次,下面我们来一行行解读:
startup: function() {this.inherited(arguments);
startup方法继承自dojox/mobile/ScrollableView,进而继承自dijit/_WidgetBase.所以我们调用this.inherited(arguments).
Startup是Dojo小部件生命周期的一部分,参见这里更多Dojo小部件生命周期:dijit/_WidgetBase Reference Guide.(http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html)
来自dijit/registry的byId方法用于查找组件引用:
// retain widgets references this.refreshButton = registry.byId("refreshButton"); this.feedList = registry.byId("feedList"); this.feedHeading = registry.byId("feedHeading"); this.detailsContainer = registry.byId("detailsContainer"); this.detailsHeading = registry.byId("detailsHeading");
ProgressIndicator是用于表明当前任务正在进行的图像,我们在等视图刷新时会用到。
this.progressIndicator = ProgressIndicator.getInstance();
这里可参考更多使用和自定义ProgressIndicator的信息:Reference Guide.(http://dojotoolkit.org/reference-guide/dojox/mobile/ProgressIndicator.html)
我们给刷新按钮增加点击事件,这里声明的this.refresh方法我们在下一篇定义。注意,使用lang.hitch方法用于确保回调函数this.refresh将在小部件实例的上下文中被调用:
// add click handler to the button that call refresh this.refreshButton.on("click", lang.hitch(this, this.refresh) );
FeedView刷新
刷新方法负责从Flickr取得照片和更新视图。首先,我们移动列表中的任何内容,然后显示等待动画:
// remove all list items this.feedList.destroyDescendants(); // reset scroll to make sur progress indicator is visible this.scrollTo({x:0,y:0}); // add progress indicator this.feedHeading.set('label',"loading..."); this.feedList.domNode.appendChild(this.progressIndicator.domNode); this.progressIndicator.start();
现在我们发送一个JSON请求给Flickr:
// request feed from Flickr this.requestOptions.query = flickrview.QUERY; scriptRequest.get(this.requestUrl, this.requestOptions).then(lang.hitch(this, this.onFlickrResponse), lang.hitch(this, this.onFlickrError));
真正更新列表的工作是我们的两个回调函数完成的:onFlickrResponse(成功响应)和onFlickrError(异常).
你可以在浏览器中输入这个请求,获取详细数据:http://api.flickr.com/services/feeds/photos_public.gne?tags=famous,bridge&lang=en-us&format=json
处理JSON响应
onFlickrResponse方法处理成功响应,我们一行行了解。首先停止进度示意动画然后更新标题:
// remove progress indicator this.progressIndicator.stop(); this.feedList.destroyDescendants(); // restore the title this.feedHeading.set('label','Feeds');
遍历结果项:
// populate the list array.forEach(result.items, lang.hitch(this, function (resultItem)
为每一项创建dojox/mobile/ListItem并加到队列的末尾:
// Create a new ListItem at the end of the list var listItem = new ListItem({}).placeAt(this.feedList, "last");
设置样式,使用简单HTML模板注入列表项内容:
// set custom style domClass.add(listItem.domNode, "photoListItem"); // create and insert content from template and JSON response listItem.containerNode.innerHTML = this.substitute(this.flickrviewItemTemplateString, { photo: resultItem.media.m, title: resultItem.title, published: locale.format(new Date(resultItem.published), {locale:flickrview.QUERY.lang}), author: resultItem.author });
先不理会published属性的格式化,我们在谈到本地化环节会详细说明。
点击列表项会触发切换到详情视图,切换之前我们需要更新详情视图的内容。所以我们给列表项增加点击处理函数:
listItem.onClick = lang.hitch(this, function(){ // update details view before transitioning to it this.detailsContainer.domNode.innerHTML = resultItem.description.replace(/href=/ig,"target=\"_blank\" href="); listItem.set("transition","slide"); listItem.transitionTo("details"); });
注意,我们加入target属性来强制所有的链接都打开新浏览页。这在链接到外网站时是最佳的实践方式。
因为我们以编程方式实现跳转,所以设置moveTo属性为#,这样小部件就不会使用自身的跳转动作。
listItem.set("moveTo","#");
异常处理比较简单:停止进度示意动画,在标题栏显示异常信息通知用户。
onFlickrError: function(error) { // remove progress indicator this.progressIndicator.stop(); this.feedList.destroyDescendants(); // display error message this.feedHeading.set('label',error); alert(error); }
接下就要测试第一个“可执行版本”的应用了!就差配置应用的本地化日期格式。
使用本地化日期格式
在JSON请求,我们指定返回数据本地化要求,但返回的数据是没有格式化的(像这样原始的形式:2013-09-15T07:57:04Z)。Dojo提供很多功能来本地化你的应用,这里我们使用一个方法来格式化:
published: locale.format(new Date(resultItem.published),{locale:flickrview.QUERY.lang}),
因为我们强制本地化(默认情况下,Dojo使用浏览器的本地化设置),所以需要指定本地化列表,Dojo必须能从配置中加载起来。我们会用到配置参数extraLocale:
dojoConfig = { async: true, baseUrl: './', parseOnLoad: false, mblHideAddressBar: true, extraLocale: ["en-us", "fr-fr", "de-de", "it-it", "ko-kr", "pt-br", "es-us", "zh-hk"], packages: [{ name: "flickrview", location: "js" }] };
最后就是应用启动时的自动刷新:
// Parse the page for widgets parser.parse(); // refresh at startup registry.byId("feed").refresh();
所有代码完成,FeedView现在从Flickr获取内容了!点击下面链接查看效果和浏览全部代码。下一节将要实现Settings view修改请求参数。
VIEW DEMO
下载源代码
下载 Part 3 - FlickrView: Implementing FeedView.
============================================
Part 3 END
next Part 4 - FlickrView: Implementing SettingsView