使用-Kendo-UI-Mobile-和-ASP-NET-Web-API--构建移动应用-全-
使用 Kendo UI Mobile 和 ASP.NET Web API 构建移动应用(全)
原文:Building Mobile Applications Using Kendo UI Mobile and ASP.NET Web API
零、前言
移动设备的突然爆发使移动应用开发成为程序员最热门的职业领域之一。多个平台共享的全球智能手机/平板电脑市场给移动应用开发者和公司提出了一个严峻的问题,因为每个移动平台都有不同的开发框架和编程语言来开发本地应用。
如何实现 WORA ( 写一次,跑哪里)?
显而易见的解决方案是 HTML5,它得到了所有最新浏览器的支持,并且非常支持开发丰富的用户界面。随着 PhoneGap 等平台的引入,基于 HTML5 的应用成为了一把双刃剑,PhoneGap 将原生操作系统 API 暴露给了用 HTML5/JavaScript 和 CSS3 开发的应用。它们可以使用一个代码库进行开发,并作为移动网站以及可以安装在不同平台上的移动应用进行部署,而不是开发本地应用,如果针对多个平台,则会产生多个代码库。业内大量理解 HTML、JavaScript 和 CSS 的网络开发人员的出现,推动了移动应用开发向 HTML5 方向的转变。随着企业和消费领域对越来越多移动应用的需求增加,不同类型的基于 HTML5 的开发框架浮出水面,并且仍在浮出水面!
来自 Telerik 的 Kendo UI Mobile 是发展最快的基于 HTML5 和 jQuery 的跨浏览器移动应用开发框架之一。KendoUI Mobile 通过提供开箱即用的自适应原生 UI 渲染,无需任何额外的编码,并支持 Kendo 基础应用开发框架,使自己与其他框架相区别。虽然其他移动开发框架只专注于用户界面部分,但剑道用户界面为端到端的客户端开发提供了支持。
随着世界越来越接近 HTTP 协议,网络应用编程接口(通过普通 HTTP 公开的服务)正在获得势头。ASP.NET 网络应用编程接口有助于构建强大的网络应用编程接口,这些接口可以被丰富的客户端使用,现在是首选.NET 开发人员构建 RESTful 应用。
这本书将向您介绍剑道 UI Mobile,并向您展示如何使用 ASP.NET Web API 作为服务后端来构建端到端的移动应用。
这本书涵盖了什么
第一章、使用 HTML5 构建移动应用,帮助一个对移动应用开发世界比较陌生的程序员,踏上了使用 Kendo UI Mobile 构建端到端应用的旅程。本章详细介绍了不同类型的移动应用,如本地、混合和移动网站,介绍了剑道用户界面移动,并总结了移动应用的设计准则。
第二章、构建你的第一个移动应用,向你详细介绍了剑道 UI 移动代码,并将开发电影票应用的一些屏幕,解释视图、布局、导航等等。从这一章开始,我们开始动手写一些非常酷的代码。
第 3 章、服务层带 ASP.NET Web API,为您介绍微软最新的新增功能之一.NET 堆栈,ASP.NET 网络应用编程接口。我们将看到路由、参数绑定、内容协商、基于令牌的身份验证、授权,并编写一些将用于示例电影票应用的 API 方法。非微软背景的读者可以跳过这一章,也可以在自己选择的平台上编写具有相同功能的服务,或者使用我们在互联网上托管的服务。前端剑道 UI Mobile 客户端应用独立于后端 API 技术,只要接受并返回相同的 JSON 数据,就可以与任何服务平台协同工作。
第 4 章、使用框架元素的集成,讨论了常见的剑道框架元素,如数据源、模板和 MVVM,它们在移动和网络应用开发中都有使用。然后,我们将通过构建用户帐户屏幕,讨论揭示模块模式和应用架构,开始与电影票应用的后端集成。
第五章、探索移动小部件,为大家介绍剑道 UI 移动框架的核心——移动小部件。我们将深入探讨诸如列表视图、按钮、按钮组等小部件。使用所提供的 js 提琴示例,用户可以玩转示例代码。
第 6 章、动作单、ModalView 和更多小部件,从上一章继续,我们将亲自探索更多剑道 UI Mobile 小部件。
第 7 章、电影票应用–完全集成,通过与剑道 UI Mobile 小部件、框架元素和 ASP.NET Web API 服务的集成,完成了我们的电影票示例应用。
这本书是给谁的
这本书是为有网络开发背景的新手和专家程序员设计的,他们希望为企业和消费者领域构建移动应用或移动网站。要很好地利用这本书,需要对 HTML、CSS 和 jQuery 有基本的了解。
这本书你需要什么
要充分利用这本书,需要以下软件:
- 剑道 UI 手机:免费试用或商业授权版(每位开发者 199 美元)
- jQuery 1.8.1 :自由
- 波纹仿真器:免费
- MS Visual Studio 2010/2012 速成版,ASP.NET MVC4:这些都是可以免费下载的,只有当你是. NET 程序员并且有兴趣探索 ASP.NET Web API 的时候才需要。
惯例
在这本书里,你会发现许多区分不同种类信息的文本风格。以下是这些风格的一些例子,以及对它们的含义的解释。
文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪 URL、用户输入和 Twitter 句柄如下所示:“现在让我们在MoviesController.cs中创建一个动作方法,它调用GetMovieList BLL 方法。”
代码块设置如下:
public class TrailerBO
{
public string MovieName { get; set; }
public string VideoUrl { get; set; }
}
当我们希望将您的注意力吸引到代码块的特定部分时,相关的行或项目以粗体显示:
<!-- Movies main view --->
<div data-role="view" id="mt-home-main-view" data-title="Movies"
data-init="MovieTickets.movieList.initialize"
data-model="MovieTickets.movieList.viewModel"
data-layout="mt-main-layout" class="no-backbutton">
任何命令行输入或输出都编写如下:
customEvent fired
customEvent fired with data: Kendo UI is cool!
新名词和重要词语以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,出现在文本中,如下所示:向左打开和向右打开两个按钮放置在视图布局中的导航栏小部件上,可以单击这两个按钮打开抽屉小部件,如以下代码所示:
注
警告或重要提示会出现在这样的框中。
类型
提示和技巧是这样出现的。
读者反馈
我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或可能不喜欢什么。读者反馈对我们开发您真正能从中获得最大收益的标题非常重要。
要给我们发送一般反馈,只需向<[feedback@packtpub.com](mailto:feedback@packtpub.com)>发送电子邮件,并通过您的消息主题提及书名。
如果你对某个主题有专业知识,并且对写作或投稿感兴趣,请参阅我们在www.packtpub.com/authors上的作者指南。
客户支持
现在,您已经自豪地拥有了一本书,我们有许多东西可以帮助您从购买中获得最大收益。
下载示例代码和图形
您可以从您在http://www.packtpub.com的账户中下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问http://www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
可以从http://www . packtpub . com/sites/default/files/downloads/0922 ot _ coloredimages . pdf下载彩色图形
勘误表
尽管我们尽了最大努力来确保我们内容的准确性,但错误还是会发生。如果你在我们的某本书里发现了错误——可能是文本或代码中的错误——如果你能向我们报告,我们将不胜感激。通过这样做,你可以让其他读者免受挫折,并帮助我们改进这本书的后续版本。如果您发现任何勘误表,请访问http://www.packtpub.com/submit-errata,选择您的书籍,点击勘误表 提交 表格链接,并输入您的勘误表详情。一旦您的勘误表得到验证,您的提交将被接受,勘误表将上传到我们的网站上,或添加到该标题的勘误表部分下的任何现有勘误表列表中。通过从http://www.packtpub.com/support中选择您的标题,可以查看任何现有的勘误表。
盗版
互联网上版权材料的盗版是所有媒体的一个持续问题。在 Packt,我们非常重视版权和许可证的保护。如果您在互联网上遇到任何形式的我们作品的非法拷贝,请立即向我们提供位置地址或网站名称,以便我们寻求补救。
请通过<[copyright@packtpub.com](mailto:copyright@packtpub.com)>联系我们,获取疑似盗版资料的链接。
我们感谢您在保护我们作者方面的帮助,以及我们为您带来有价值内容的能力。
问题
如果您对本书的任何方面有问题,可以在<[questions@packtpub.com](mailto:questions@packtpub.com)>联系我们,我们将尽最大努力解决。
一、使用 HTML5 构建移动应用
世界正在走向移动化,每天都有数百万部智能手机被激活。因此,为消费者开发的移动应用越来越多,企业软件产品也在缓慢但稳步地加入这场革命。越来越多的企业确信,为了维持,他们需要在移动领域。你是一名网络开发人员,在一个晴朗的早晨,你的经理告诉你,“我们现在需要专注于移动平台。准备好!”或者你想开发一个需要部署到一个或多个移动应用商店的移动应用。在选择合适的移动开发平台时,有许多因素起作用,例如您的开发技能、原生功能、安全性、离线能力以及对多个平台的支持。在本章中,我们将看到开发移动应用的不同方法,并了解为什么 HTML5 是跨平台开发的流行选择。然后,我们将介绍剑道用户界面移动,并将讨论一些移动应用的设计原则。
在本章中,我们将介绍:
- 本地网站、混合网站和移动网站
- HTML5 和 CSS3
- 剑道用户界面——轻松构建跨浏览器应用
- 剑道用户界面手机
- HTML5 移动网络应用设计指南
本地对混合对移动网站
您可能已经知道,有三种方法可以开发与移动设备兼容的应用:本机、混合或移动网站。
原生应用 是用特定平台的编程语言编写的,即 iOS 的 Objective C、安卓的 Java 等等。本机应用运行更快,可以访问所有设备 API 和功能,并提供更好的用户体验。由于原生应用是为特定平台构建的,如果原生应用需要在另一个平台上运行,则需要重写整个应用。这将造成代码重复、维护难题、预算增加,以及需要多个开发团队(专门负责某个平台)处理相同的业务规则。通常,需要高性能的游戏和应用是作为纯本机应用开发的。
混合应用 是使用纯网络技术(如 CSS、HTML 和 JavaScript)编写的,它们使用设备的浏览器引擎在设备的本地容器中运行。混合应用通常使用 PhoneGap 等工具打包,这有助于应用访问特定于设备的应用接口和硬件功能。WebKit 渲染引擎用于 iOS、Android 和 Blackberry 等平台,渲染要在原生平台的 web 视图控件中显示的基于 web 的脚本/代码。由于混合应用是使用本机应用外壳创建的,因此它们也可以使用应用商店进行分发。Telerik 的 Icenium 是一个基于云的一体化开发环境,用于打包、测试和部署混合应用。PhoneGap 是另一个常用的工具,用于打包网络应用,以便在多个移动平台上部署。
移动网站 使用网址访问,并在移动设备的浏览器中运行。它们可以使用服务器端技术开发,例如 ASP.NET 和 PHP,并且可以在没有任何应用商店批准过程的情况下进行部署和更新。如果移动网站是使用纯 web 技术(HTML、CSS 和 JavaScript)编写的,或者使用基于这些技术的框架编写的,例如 jQuery、Kendo UI 和 jQuery Mobile,那么移动网站可以打包安装为混合应用,无需太多努力。移动网站的一个主要缺点是只能访问通过浏览器公开的设备功能,这使得移动网站适合基于内容的应用。没有一个单一的解决方案适合所有的场景。方法的选择将取决于许多因素,如您拥有的技能集、预算、时间表和更新频率。
剑道 UI Mobile 是开发混合应用和移动网站的理想平台。在本书的过程中,我们将使用剑道用户界面手机创建一个电影票应用,该应用最初将被视为一个移动网站,然后在本书的结尾,它将使用 PhoneGap 作为一个移动应用进行打包,并部署到安卓和 iOS 设备上。
HTML5 和 CSS3
HTML5、jQuery 和 CSS3 是开发尖端 web 应用的新咒语。当我们说网络时,它包括标准网站/应用、移动网站/应用以及混合应用。即使这本书是关于剑道 UI Mobile 的,但了解一下剑道 UI Mobile 框架背后的底层技术也很重要,即 HTML5、CSS3 和 jQuery。在我们深入研究剑道 UI Mobile 之前,让我们先简要地从高层次上看一下 HTML5 和 CSS3 技术。因为我们希望这本书的读者对 jQuery 有所了解,所以我们不会在这里讨论 jQuery 的基础知识。
html 5–史蒂夫·乔布斯让我出名
当史蒂夫·乔布斯在 iOS 设备上著名地宣布 Flash 的死亡并认可 HTML5 是未来时,HTML5 在互联网公众中一举成名,因为它有助于在不依赖第三方浏览器插件的情况下构建高级图形、排版、动画和过渡。
HTML5 是一种标记语言规范,由大量特性、技术和 API 组成,允许内容开发人员创建丰富的交互体验。HTML5 仍处于推荐阶段,但许多浏览器已经实现了规范的很大一部分。这对开发人员来说是一个挑战,因为他们需要在启动 HTML5 项目之前弄清楚哪些浏览器支持哪些功能。
html 5 的主要特点
尽管【HTML5 的规范还没有完成,但是大量的特性已经被主流浏览器实现了。以下是 HTML5 目前的一些主要特性:
|- Application cache supports offline network applications.
| * geographical position |
- Events sent by the server
|
|
- audio and video
|
- Index database
|
- Web page API
|
|
- 【画布应用编程接口】
|
- 【mathml】
|
- Network storage
|
|
- Cross-document message
|
- Micro data
|
- web workers
|
|
- drag and drop
|
- Scalable vector graphics ( SVG )
|
- XMLHttpRequest 二级
|
注
谷歌的 http://html5rocks.com是一个非常好的 HTML5 参考网站,有大量的教程、文章和其他资源。
html 5 规范背后是谁?
HTML5 规范的开发背后有三个不同的组织: W3C ( 万维网联盟)WHATWG(网络超文本应用技术工作组)和 IETF ( 互联网工程任务组)。因此,W3C 和 WHATWG 维护的 HTML5 规范有两个版本,分别可以在http://www.w3.org/TR/html5/和http://whatwg.org/html找到。 IETF 由负责 HTTP 等互联网协议的小组组成,处理 HTML5 的 WebSocket API 使用的 WebSocket 协议。
HTML5 的两个不同版本并不令人担忧,因为 WHATWG 版本被认为是一种生活标准(意味着将会不断发展,不再应用版本),W3C 正计划创建一个单一的最终标准,WHATWG 称之为他们生活标准的快照。
WHATWG 的工作重点是开发 HTML 和相关技术的规范描述(意味着修复 bug、添加新特性和通常跟踪实现),而 W3C 将继续 HTML5 规范的工作,专注于单一的权威标准。
【HTML5 页面示例
现在让我们把的手弄脏,看看一个简单的 HTML5 页面:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Our first HTML5 page </title>
</head>
<body>
<header>
<h1>Sample HTML5 Structure</h1>
<nav>
<ul>
<li><a href="#">Link1</a></li>
<li><a href="#">Link2</a></li>
</ul>
</nav>
</header>
<section>
<h1>Main Section</h1>
<h2>This is a sample HTML5 Page</h2>
<article>
<p>Article 1 goes here</p>
</article>
<article>
<p>Article 2 goes here</p>
</article>
</section>
<footer>
<p>Footer goes here</p>
</footer>
</body>
</html>
类型
下载示例代码
您可以从您在http://www.packtpub.com的账户中下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问http://www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
DOCTYPE 和字符编码
HTML5 的DOCTYPE声明非常简单:<!DOCTYPE HTML>
这一行需要添加到我们创建的每个 HTML5 页面的顶部。新的DOCTYPE声明简单明了,不同于 HTML4 中冗长难记的声明,如下所示:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
所有最新的浏览器都会查看新的DOCTYPE声明,并将内容切换到标准模式。
与DOCTYPE声明一样,字符集声明也在 HTML5 中进行了简化,如下所示:
<meta charset="UTF-8">
元元素中的meta charset属性被用来代替 HTML4 中的 pragma 指令。
这是 HTML4 中典型字符编码的外观:
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
一些新的 HTML5 标签
正如在前面的代码中所强调的,您可以看到一些新的 HTML5 标签,它们是语义标记。语义标记向浏览器和开发人员清楚地描述了它们的意义或目的。在 HTML4 中,我们使用<div>标签来定义页面中的划分,但它从未提供任何关于内容的细节,也没有特定的含义。为了有意义,开发人员通常会添加一个id属性,并提供一个有意义的标识,如sectionHeader、footer和navLinks,如下所示:
<div id="sectionHeader"> </div>
像<header>、<footer>、<nav>这样的语义元素被添加到 HTML5 中,作为挖掘数十亿个网页的结果,用于找出开发人员最常使用的常见 id 和 CSS 类名,并用于选择一个小子集添加到 HTML5 规范中。这非常有意义,因为这将有助于搜索引擎和辅助工具轻松抓取网页,开发人员可以处理整洁的 HTML 代码。
现在让我们检查一下我们在示例 HTML5 页面中使用的一些新的语义标记。
<表头>
<header>元素代表一组介绍性或导航辅助工具。通常<header>将是页面中的第一个元素。它通常包含该部分的标题(h1–h6 元素或 hgroup 元素),但这不是必需的。header 元素还可以用于包装一个部分的目录、搜索表单或任何相关的徽标。
<导航>
<nav>元素代表一个部分,该部分包含指向其他页面或页面内部分的导航链接。该元素主要用于包含主要导航块的部分。通常页脚会有一个简短的链接列表,链接到网站的不同页面。在这种情况下,仅使用<footer>元素就足够了,而<nav>元素也可以使用,但通常是不必要的。
以下是您可以考虑添加<nav>元素的更多地方:
- 面包屑
- 目录
- 侧面导航
<段>
<section>元素代表文档或应用的通用部分。在这种情况下,一节是由以下内容组成的专题组:
- 回
- 选项卡式对话框中的各种选项卡页
- 论文的编号部分
一个网站的主页可以分为介绍、新闻和联系信息几个部分。它是您将考虑作为记录存储在数据库中的一段内容。
只有在文档大纲中明确列出内容时,<section>元素才是合适的。
<篇>
<article>元素代表独立的内容部分,如博客文章、评论和杂志文章。一篇文章应该是一个独立的实体,即使周围的内容被移除,也应该可以分发或重用它。
<页脚>
<footer>元素表示包含元素的信息,例如相关内容的链接或版权信息。即使这是通常的做法,也不需要在这一节的末尾添加页脚。
注
http://html5test.com/是一个测试浏览器 HTML5 兼容性的好网站。
CSS3
CSS3 是 CSS 的最新版本,与 CSS2 不同(CSS2 是 CSS 的单个大规格)被分成多个模块,这些模块被分别记录和处理。CSS3 规范的工作早在 1998 年 CSS2 的规范一完成就开始了,现在还在更新中。
CSS3 通过制作网页元素的动画、应用不同的效果(如渐变、阴影、多种背景和不透明度)等,帮助网页增添活力,而无需像以前版本的 CSS 那样使用图像或客户端代码。CSS3 有助于提高应用的性能,因为 CSS 文件缓存在客户端,并在支持的浏览器上使用硬件加速技术进行动画。
CSS3 有超过 50 个来自 CSS 工作组发布的模块,如媒体查询、名称空间、颜色、动画、背景和边框、选择器和 2D/三维转换。
让我们用一个快速的例子来看看,用 CSS3 对 HTML 元素应用圆形边框、变换和阴影是多么容易。
首先,让我们定义一个简单的div元素,并给它添加一些样式:
<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">
div#myDiv
{
width: 200px;
height: 100px;
background-color: #A3D1FF;
border: 1px solid black;
margin: 10px;
padding: 5px;
}
</style>
</head>
<body>
<div style="margin: 75px">
<div id="myDiv">
I am an HTML div
</div>
</div>
</body>
</html>
HTML 的呈现如下图所示:

现在,我们需要将这个 div 旋转 40 度,给边框添加圆角,给框添加阴影。在 CSS3 中,使用几行代码就可以非常容易地达到这些要求。向我们的 HTML 文档的 CSS 定义中添加以下 CSS3 属性:
border-radius: 10px;
box-shadow: 8px 8px 1px gray;
transform: rotate(40deg);
-ms-transform: rotate(40deg); /* IE 9 */
-webkit-transform: rotate(40deg); /* Safari and Chrome */
在重新加载 HTML 页面时,我们可以看到我们的 div 已经转换为新的外观:

剑道 UI——轻松构建跨浏览器应用
现在我们已经有了足够的概述,让我们开始真正的东西:来自 Telerik 的剑道用户界面框架。Telerik 在为开发人员提供快速应用开发 ( RAD )工具方面一直走在前列,Kendo UI 框架是 Telerik RAD 堆栈的最新成员,用于构建基于 HTML5、jQuery 和 CSS3 的跨浏览器应用。
使用剑道 UI 唯一的外部依赖是 jQuery。使用剑道用户界面开发应用很简单,设置只需要在您的 HTML 页面中引用 jQuery、剑道 JavaScript 和 CSS 文件。
注
剑道这个词的意思是“剑道”,这是日本传统的剑术。
剑道用户界面框架可以大致分为:
- 剑道用户界面小部件
- 剑道用户界面框架元素
剑道小部件使用 HTML5、CSS3 和 jQuery 来构建强大的网络应用,而不使用多个第三方框架和插件,就像其他一些 HTML5/jQuery 框架一样。
剑道 UI 小部件是应用开发后我们真正“看到”的 UI 元素,例如按钮、下拉列表和树形视图。框架元素是不可见的实体,如数据源、模板和 MVVM,这有助于将数据与小部件集成在一起。为了简单起见,如果我们考虑一个使用剑道用户界面构建的应用,用户界面小部件是砖块,而框架元素是帮助砖块粘在一起的水泥。
剑道 UI 小部件
剑道 UI 小部件可分为三组:
-
剑道 UI Web :这些是用于支持触控的桌面开发
-
剑道 UI 数据可视化:用于桌面和移动数据可视化
-
Kendo UI Mobile: These are used for mobile application development
![Kendo UI widgets]()
剑道 UI 架构
尽管剑道 UI Web 和 DataViz 不在本书的讨论范围内,但是了解这两者也是一个好主意,因为所有三个小部件组无缝地相互操作,并且在一个大项目中很有可能需要使用多个小部件组。
剑道 UI 网
剑道 UI Web 小部件作为用于构建键盘/鼠标输入,以及基于触控的传统跨浏览器 Web 应用。截至今天,剑道 UI Web 小部件集合中有 22 个小部件可用。所有剑道用户界面网络部件都完全支持触摸屏设备,如 iPad、iPhone 和安卓,因此可以在各种输入选项不同的设备上访问网络应用。
尽管剑道 UI Web 框架使用了基于现代 HTML5 和 CSS3 的小部件,但它通过实现优雅的降级来支持 IE7 和 IE8 等较旧的浏览器。
注
更多关于剑道 UI 网的信息可以在http://www.kendoui.com/web.aspx找到
剑道 UI DataViz
剑道 UI DataViz 小部件有助于使用 HTML5 构建尖端的、触摸感知图表和仪表盘。得益于内置的硬件加速,DataViz 小部件使用更少的 CPU 资源,从而为所有动画和渲染提供最大的性能。使用 SVG 在客户端呈现 DataViz 图形,对于传统浏览器,可以回退到 VML。
DataViz 支持以下图表和仪表小部件:
|图表类型
|
仪表类型
|
| --- | --- |
| 面积 | 射线 |
| 酒吧 | 线性的 |
| 泡泡 | |
| 线条 | |
| 甜甜圈 | |
| 馅饼 | |
| 分散 | |
注
就像网络小部件一样,DataViz 通过混合有限的 HTML5 支持(如果有的话)和适度的降级来支持 IE7 和 IE8 等旧浏览器。DataViz 完全支持最新的移动浏览器,因此它既可以用于桌面网络应用,也可以用于移动应用。更多关于剑道 UI DataViz 的信息可以在:http://www.kendoui.com/dataviz.aspx找到
剑道 UI 手机
剑道 UI Mobile 是剑道 UI widget 栈的第三组,帮助构建基于 HTML5 的移动网站以及混合移动应用。剑道用户界面移动版采用自适应渲染技术,帮助应用的外观和感觉适应查看它的平台。没有任何配置或额外的代码,一个建立在剑道 UI Mobile 上的应用看起来就像安卓设备上的安卓,iOS 设备上的 iOS,等等。在撰写本书时,剑道 UI Mobile 支持 iOS、安卓、黑莓和 Windows 8 平台。
现在,您可能会有这样一个问题:“如果我需要在查看应用的所有设备上有单一的外观和感觉,该怎么办?”最近,剑道发布了一个通用移动主题 ,开发者可以使用它在不同的平台上创建一致的外观和感觉。如果你没有使用这个新的主题,那么在实例化移动应用的时候,一个单一的配置来强制一个特定的平台就可以了!一旦我们强制用户界面使用特定平台的外观进行渲染,然后进行修改,该平台的相应 CSS 文件将确保跨多个平台的相同外观。
注
剑道还提供了移动主题构建器来定制移动小部件的样式:http://demos.kendoui.com/mobilethemebuilder/index.html
服务器包装器
如果你是一个在 ASP.NET MVC、JSP 或 PHP 上工作的服务器端程序员,有一个好消息要告诉你:剑道 UI 有可用于 ASP.NET MVC、PHP 和 JSP 的服务器包装器,它会自动生成必要的 HTML 和 JavaScript 来配置、渲染和初始化你的剑道 UI 小部件(Web 和 Mobile)和图表(DataViz)。服务器包装器发出剑道用户界面 JavaScript,并提供对剑道用户界面客户端应用编程接口的完全访问,以便在呈现网页后也可以从客户端操作用户界面。
注
你可以在这里阅读更多关于服务器包装的信息:http://www.kendoui.com/server-wrappers.aspx
HTML5 移动网页应用设计指南
为移动设备开发应用与为台式机和笔记本电脑开发网站截然不同。有各种不同屏幕尺寸和处理能力的移动设备可供选择。移动设备以不同的带宽接入互联网;他们可以连接到无线网络,并在瞬间进入慢速的 2G 网络。所有这些因素使得移动应用开发不同于传统的 web 应用开发。
以下是一些指导方针,它们将在构建移动网络应用时帮助开发人员:
- 使用 CSS3 而不是 jQuery 的动画:尽可能使用 CSS3 动画,因为它们是在浏览器中本地处理的,这样它可以利用硬件资源,从而提高性能。
- 使用 CSS3 媒体查询的响应性设计:如今的移动设备的屏幕大小各不相同,基本的预期是,移动网络应用将适合请求应用的设备的屏幕。使用 CSS3 媒体查询的响应设计有助于相同的代码库在各种设备上提供最佳的观看体验。
- 使用 CSS3 渐变代替图片:使用 CSS3,我们可以在你的网页中提供渐变,而不需要使用特别裁剪的图片。这有助于保留带宽,因为图像不会在网络上传播,并为页面设计提供了更大的灵活性。
- Go for SPA(单页应用) : SPA(例如 Gmail)为用户提供了更快、更灵敏的导航体验。它们的特点是能够重新绘制用户界面的任何部分,而不需要服务器往返来检索页面。在构建移动应用时,如果 SPA 不会使您的开发过程变得复杂,请选择它。Kendo UI Mobile 是一个构建单页应用的优秀框架,您将在以后的章节中看到。
- 充分利用离线模式和本地数据存储:现在几乎所有的现代移动浏览器都支持相当多的 HTML5 规范,因为它们的更新频率与桌面同类相比非常高。虽然我们可以有把握地假设这些功能将在移动浏览器上可用,但最好使用简单的 JavaScript 条件语句检查这些功能是否在客户端浏览器上启用。
总结
在本章中,我们讨论了开发移动应用的不同方法,并介绍了 HTML5 和 CSS3。我们还通过在 HTML5 和 CSS3 中编写一些示例代码而弄脏了手。了解 HTML5 和 CSS3 结合的力量很重要,这将为下一代应用提供最强大的移动应用开发工具。然后,我们对剑道用户界面架构进行了高层次的研究,并完成了 HTML5 移动应用的一些设计指南。
在下一章中,我们将深入到剑道 UI Mobile,编写一些代码,并开发一个应用的几个屏幕,当我们阅读这本书时,该应用将被开发为一个功能齐全的网络应用。
二、构建您的第一个移动应用
在这一章中,我们将弄脏我们的手,用一些剑道用户界面移动代码热身,然后开始构建一个名为电影门票的端到端移动应用。在这本书的过程中,我们将发现新的特性并增强我们的应用,使其成为一个成熟的、可部署的移动应用!最初我们将这个应用作为单页应用来对待,可以托管在 web 服务器上,也可以通过在浏览器本地加载index.html文件来访问,以减轻调试和测试的痛苦。
准备显影机
设置环境开发剑道 UI 手机应用简单明了。创建一个基础 HTML 文件,添加所需的.js和.css剑道 UI Mobile 文件引用,就可以开始了。除非您想使用诸如 Visual Studio、Dreamweaver 或 Komodo 之类的 HTML IDE,否则您只需要一个文本编辑器来开发 Kendo UI 应用。剑道 UI 唯一的外部依赖是 jQuery。始终检查您正在使用的剑道 UI 版本所支持的 jQuery 版本。不能保证最新版本的 jQuery 会得到最新版本剑道的支持。所以最安全的方法是使用剑道 UI 包提供的 jQuery 文件。
您可以使用任何带有 HTML5 支持的浏览器的操作系统来开发剑道 UI Mobile 应用。我们将在本书中遵循的开发移动应用的过程将是在桌面机器上进行开发,并在模拟器和移动设备上作为移动网站运行。我们将使用的集成开发环境是 Visual Studio 2012,模拟器将是来自动态研究 ( RIM )的 Ripple。如果你不习惯使用 Visual Studio,请随意使用任何 HTML 编辑器或文本编辑器;你几乎感觉不到任何不同。
类型
如果你没有微软 Visual Studio,可以从这个链接下载免费的 Visual Studio Express 2012 网络版或者其他口味的试用版:http://www.microsoft.com/visualstudio/downloads
波纹仿真器
移动应用开发不同于桌面应用开发,前者在桌面上开发,部署在移动设备上,后者在台式电脑上开发使用。在移动设备上测试和调试应用并不简单,有时几乎不可能。因此,仿真器在测试和调试桌面上的移动应用方面发挥着关键作用。
Ripple Emulator 是市场上首选的模拟器之一,可模拟安卓、iOS 和黑莓等多种移动平台。Ripple 提供了调试 HTML5 移动应用的能力,充分展示了该应用在不同平台下的外观。Ripple 帮助开发人员实时实现自动化测试、JavaScript 调试以及多设备和屏幕分辨率仿真,而无需重新部署移动应用或重启仿真器。
Ripple 作为谷歌 Chrome 浏览器的扩展运行,所以你需要在你的机器上安装谷歌 Chrome。
安装波纹模拟器
Ripple 的安装非常简单,可以通过以下简单的步骤完成:
-
如果谷歌浏览器尚未安装,请安装它,并导航到谷歌浏览器商店:https://chrome.google.com/webstore/。
-
Search for Ripple Emulator.
![Installing Ripple Emulator]()
-
现在点击添加到铬合金按钮。
-
Click on Add when the following popup appear:
![Installing Ripple Emulator]()
-
Ripple will now be installed on your Chrome browser and can be enabled/disabled using the Ripple icon that appears on the top-right corner of the browser:
![Installing Ripple Emulator]()
-
Now let's visit the mobile website of reuters.com and see Ripple in action. Enter http://mobile.reuters.com/ on the browser address bar and when the website loads, click on the Enable button. If Ripple asks to select a platform to test with, select a platform of your choice. I am selecting Apache Cordova/PhoneGap so that we can emulate the behavior of a mobile app bundled using Cordova/PhoneGap. Once inside the emulator, you can select a device from the Devices list on the left. Let's select iPhone 5 and see how reuters.com appears on iPhone 5:
![Installing Ripple Emulator]()
Ripple 附带了许多功能,例如更改设备方向、摇动设备、设置地理位置以及触发特定于平台的事件,这有助于开发人员在桌面上测试和调试移动应用。我们强烈建议在这一点上玩一会儿 Ripple Emulator,感受一下它的所有特性。
类型
要使用 Ripple Emulator 在本地测试您的移动应用,需要使用本地网络服务器将它作为网站托管。要了解更多关于 Ripple 的信息,请访问:https://developer . blackberry . com/html 5/documentation/get _ start _ with _ Ripple _ 1866966 _ 11 . html
当然,没有什么可以取代移动设备上的实际测试。在许多情况下,剑道用户界面移动风格在移动设备上比在桌面浏览器上看起来更好。如果您对正在开发的应用是认真的,请确保在部署应用之前,您已经接触了所有的目标设备,并进行了几轮测试。
首款剑道 UI 手机应用
学习任何新技术或新语言的最好方法就是深入其中,把你的手弄脏。因为这本书是关于越来越多的代码,让我们从创建一个简单的单页应用开始。
我们将遵循渐进的增强方法,从基本元素开始,增加应用的复杂性,最后,我们将创建一个功能齐全的可部署应用。
注
Kendo UI Mobile 仅支持 WebKit 浏览器,因此使用 Chrome 或 Safari 浏览器在桌面上运行演示代码非常重要。
现在您已经准备好了 IDE 和模拟器,请执行以下步骤来运行您的第一个移动页面:
-
从以下网址下载剑道 UI:http://www.kendoui.com/download.aspx。您可以购买剑道 UI 许可证,也可以下载 30 天免费试用版。
-
为应用创建一个
root文件夹,并将以下手机专用文件/文件夹从剑道包复制到该文件夹:jquery.min.js和kendo.mobile.min.js文件从js文件夹到应用根目录的js/kendo文件夹kendo.mobile.all.min.css和images文件夹从styles文件夹到应用根目录的styles/kendo文件夹
-
Create an
index.htmlfile in therootfolder with the following code in it:<!DOCTYPE HTML> <html> <head> <script src="js/kendo/jquery.min.js"> </script> <script src="js/kendo/kendo.mobile.min.js"></script> <link href="styles/kendo/kendo.mobile.all.min.css" rel="stylesheet" /> </head> <body> <div data-role="view" data-title="Movie Tickets" id="mt-home-main-view" data-layout="mt-main-layout"> Home Page View </div> <div data-role="layout" data-id="mt-main-layout"> <header data-role="header"> <div data-role="navbar"> <span data-role="view-title">Movie Tickets</span> </div> </header> <footer data-role="footer"> <span style="color:#fff"> Footer content goes here </span> </footer> </div> <script> var application = new kendo.mobile.Application(); </script> </body> </html>注
剑道在内容交付网络 ( CDN ) 中托管开发所需的 JS 和 CSS 文件的精简版本,可通过以下方式访问:
http://cdn.kendostatic.com/<版本>/js/<文件名>.min.jshttp://cdn.kendostatic.com/<版本>/styles/<文件名>.min.css例如:
http://cdn.kendostatic.com/2013.2.716/styles/kendo.mobile.all.min.csshttp://cdn.kendostatic.com/2013.2.716/js/kendo.mobile.min.js -
Configure your local web server so that
index.htmlcan be accessed using a URL; something likehttp://localhost/movietickets/index.html注
如果不熟悉如何在 IIS 中创建虚拟目录,可以在这里找到:http://goo.gl/0fUO1。如果您使用的不是 Windows 操作系统,请从互联网上大量的手册中获得帮助,这些手册解释了如何在本地托管网站。
请始终记住,只有当您需要使用 Ripple Emulator 测试移动应用时,这一步才是必需的。您总是可以在兼容 HTML5 的浏览器(如 Safari 或 Chrome)中打开您的网站,看到您的应用正在运行,并使用它们的调试工具。
类型
WebKit 是一个开源的浏览器渲染引擎,被 Safari 和 Chrome 等浏览器使用。因此,如果您打算使用 PhoneGap 或任何其他基于 WebKit 的移动开发框架捆绑您的应用,那么使用任何基于 WebKit 的浏览器测试该应用通常都是安全的,因为它们共享相同的呈现引擎。
-
打开 Chrome,导航到我们之前创建的
index.html文件。点击瑞波的启用按钮,选择该设备作为最新版本的 iPhone。如果要求平台,请随意选择任何平台,如 PhoneGap 或移动网络。我们正在选择Apache Cordova/phone gap(1 . 0 . 0)和iPhone 5(Ripple 中的 PhoneGap 2.0.0 版本可能会导致屏幕滚动,这不是想要的行为)。
恭喜,你的第一款使用剑道 UI Mobile 的手机应用已经上线运行!

类型
如果您计划使用本地包装器(如 Cordova/PhoneGap)捆绑您的移动应用,请始终在本地捆绑所需的脚本文件,并避免使用 CDNs,因为这将大大延迟我们应用的加载时间。
视图和布局
视图小部件代表剑道用户界面移动应用中的一个页面。所有小部件和其他 HTML 元素都被添加到视图中。任何剑道 UI 手机应用都会有一个或多个视图。
一个data-role属性被用来定义一旦 HTML 元素被渲染,它会变成什么样的移动小部件。
属性定义data-role="view"定义了一个视图,data-layout属性用于定义哪个模板将作为我们视图的布局,如我们前面的示例所示:
<div data-role="view" id="mt-home-main-view" data-layout="mt-main-layout"> Home Page View </div>
布局只不过是一个主包装器,视图将被渲染到其中。
当一个视图被初始化时,视图中的所有移动、网络和数据可视化小部件都按照这个顺序被初始化。
在前面的代码中,我们已经在布局中声明了另外两个角色,页眉和页脚,它们将分别呈现为视图的页眉和页脚。
类型
需要注意的一点是,由于剑道 UI Mobile 的设计使得应用自动适应其运行的移动平台的原生外观和感觉,因此由于操作系统 UI 设计惯例,安卓和 iOS 设备的页眉和页脚位置是相反的。在 iOS 中,页眉显示在屏幕顶部,页脚显示在屏幕底部,而安卓设备中的相同代码将在顶部显示页脚,在底部显示页眉。
适航
我们遇到的下一个小部件被定义为data-role = "navbar",正如它的定义所暗示的,它是导航栏或导航栏小部件,我们可以在其中添加标题和/或其他小部件。我们将很快看到如何将其他小部件添加到导航栏中,但是现在我们只添加应用的标题。
通常,导航栏是在布局中添加的,因此根据加载的视图,导航栏的标题需要更改。剑道提供了一种简单的方法。
只需在导航栏中添加一个属性为data-role="view-title"的 HTML 元素。当视图更改时,设置为的值视图标题(使用视图的data-title属性)将自动显示在用view-title角色修饰的元素内的导航栏中:
<!-- navbar definition -->
<div data-role="layout" data-id="my-layout">
<div data-role="navbar">
<span data-role="view-title">My View Title</span>
</div>
</div>
<!-- view 1 -->
<div data-role="view" data-layout="my-layout"data-title="View 1 Title"></div>
<!-- view 2 -->
<div data-role="view" data-layout="my-layout"data-title="View 2 Title"></div>
应用初始化
我们前面讨论的 HTML 代码只包含小部件的定义,为了让它们变得生动起来,我们需要将我们的应用初始化为剑道 UI Mobile 应用。这是用一小段神奇的代码完成的:
var application = new kendo.mobile.Application();
这段代码初始化了你的剑道用户界面移动应用,并赋予了所有小部件生命,它需要在</body>标签之前添加。
应用初始化脚本非常强大,可以扩展到设置应用的许多全局属性,我们将在本章的后面进行探讨。
类型
所有剑道用户界面网络部件都经过高度触控优化,并设计为在移动设备上运行。因此,如果需要的话,您可以在移动应用中使用网络小部件。
一款真实世界的手机应用——电影票
现在让我们进入真正的交易,使用剑道 UI Mobile 开发一个功能齐全的移动应用,看看它如何与 RESTful 服务集成(我们案例中的 ASP.NET Web API 将在第 3 章、服务层与 ASP.NET Web API中介绍)。
在我们开始构建电影票应用之前,让我们快速浏览一下将在高级应用中实现的屏幕和功能:
- 用户可以搜索特定的电影,然后选择一个剧院进行放映。
- 一旦用户选择了演出时间,应用就会向用户询问门票数量并显示价格。一旦输入票据数量,总金额将自动更新。
- 预订后,用户可以在预订历史视图中看到已完成的预订。
- 在用户帐户屏幕中,用户可以更新姓名、电子邮件标识、地址等。
- 在预告片选项卡中,显示了视频预告片,可以使用滑动手势进行导航。
主屏幕
现在让我们使用我们开发的单屏幕应用来构建我们移动应用的主屏幕。我们将在屏幕的页脚添加一个标签条,这样中间的内容可以滚动,标签条保持静态。在标签条中,我们将添加标签,如电影、预告片、我的帐户和关于。然后,我们将为这些屏幕创建视图,并在单击选项卡条上的链接时导航到相应的视图。
完成的电影票应用如下所示:

您可以在下面的截图中看到预订和预告片页面:

选项卡条小部件
剑道用户界面移动框架附带了许多有用的小部件,有助于快速构建外观精美的应用。我们将在这里快速浏览一下 tabble 小部件,因为它是框架中最重要的小部件之一,我们将在第 5 章、探索移动小部件中详细探索其他小部件。
TabStrip 小部件通常用于在布局页脚元素中显示一组导航按钮。单击导航按钮,将加载相应的视图。
当导航到另一个视图时,它会根据当前视图的网址更新选项卡条当前选定的选项卡。标签条带有内置的标签图标,可以通过设置锚元素的data-icon属性来使用,或者我们可以通过在锚元素内添加img标签来添加其他图像。
以下是剑道提供的所有内置图标列表:
| 关于;在…各处 ;大约行为增加书签照相机手推车构成联系人细节下载快进 | 收藏夹作为特色的顶级的地球历史家信息更多采样最受关注组织 | 中止玩最近恢复精神回答重绕搜索设置分享停止废物 |让我们打开之前创建的视图,并在替换当前页脚内容后向页脚添加以下代码:
<footer data-role="footer">
<div data-role="tabstrip">
<a href="#mt-home-main-view">
<img src="img/movies.ico"
height="40" width="40" /><br />
Movies
</a>
<a href="#mt-about-view" data-icon="about">
<br />
About
</a>
</div>
</footer>
现在将书籍的source code文件夹中的images文件夹复制到您工作区的根目录下,这样您就可以看到我们正在使用的所有图像。
这里我们使用了一个自定义图标和一个内置图标作为导航按钮。href属性的视图标识值前面加上了#。这告诉应用,当点击这个按钮时,我们需要导航到一个特定的视图。
同一文件中视图之间的导航是导航的基本层次,很快我们将看到如何导航到外部网址、远程视图(外部文件中的视图)以及查询字符串中的数据传输。
点击电影导航按钮将带您进入该应用的主页,点击关于图标将带您进入关于视图,我们将在关闭第一个视图的div后添加该视图,如下所示:
<!-- about view -->
<div data-role="view" id="mt-about-view" data-title="About" data-layout="mt-main-layout">
<div style="padding: 15px">
This is a sample application developed for the book
Building Mobile Applications using Kendo UI Mobile and ASP.NET Web API
</div>
</div>
代码非常不言自明。为同一文件中的关于视图添加了具有相同布局的新视图。
当使用 Ripple Emulator 或 iPhone 查看时,这两个屏幕将如下所示:

过渡
Kendo 在浏览视图时提供了种不同的过渡效果,如果您需要跨应用的单一类型的过渡,我们可以在 app 初始化脚本中进行配置,如下所示:
var application = new kendo.mobile.Application(document.body, {
transition: 'fade'
});
剑道 UI Mobile 支持的不同过渡效果有:
- 缩放:当前视图淡出,新视图连同页眉和页脚一起从屏幕中心进行缩放。
- 滑动:此为默认过渡。使用
slide :(direction)可以指定过渡方向。方向参数值可以是left或right。当前视图向指定方向滑动,新视图取而代之。如果没有指定方向参数,则默认方向参数为left。 - 叠加:新视图在当前视图的顶部滑动,不转换页眉和页脚。
overlay:(direction)参数可以用来指定过渡方向,可能的值有left、right、up和down。比如transition: 'overlay:up'。 - 淡入:新视图及其页眉和页脚内容淡入当前视图内容的顶部。
参数reverse可以和转场效果一起指定,反向播放转场。如果我们在一个视图中指定slide:right reverse并尝试导航到上一个视图,上一个视图将从左侧滑入。
航行
在前面的例子中,我们已经看到了如何使用anchor标签中的href属性在本地视图(同一文件中的视图)之间进行导航。现在我们来详细探讨一下剑道 UI Mobile 的导航框架。
远程视图
在开发一个有很多屏幕的大应用时,将一个应用的所有视图保存在同一个物理文件中是不切实际的。几乎总是,开发人员希望每个屏幕/模块都有自己的 HTML 文件,这将有助于代码维护,并在程序员团队处理应用时简化开发。
在剑道 UI Mobile 中处理这种情况很简单。如果我们需要加载一个远程/外部视图,只需要指定用于导航的外部视图的文件名,而不是视图的 ID。导航到远程视图时,Kendo UI Mobile 使用 Ajax 调用加载视图,缓存文件内容,并显示文件中遇到的第一个视图。如果文件中有额外的视图,它们将被追加到文档对象模型 ( DOM ) 中,但不会被初始化或显示。任何内联样式/脚本元素和移动布局定义也将被评估并附加到应用中。
为了看到这一点,让我们在root目录中创建一个名为Trailers.html的文件,其内容如下:
<div data-role="view" data-title="Trailers" data-layout="mt-main-layout" id="mt-trailers-view">
</div>
如您所见,没有必要添加剑道参考文件,因为这些文件已经从index.html文件中引用。
现在我们有了一个外部视图,让我们使用 TabStrip 小部件导航到这个外部视图。打开index.html,在搜索和电影按钮之间出现一个新的导航元素,如以下代码片段所示:
<a href="Trailers.html">
<img src="img/trailers.ico" height="40" width="40" /><br />
Trailers
</a>
现在在模拟器或移动设备上重新加载index.html,点击预告片图标,您可以看到该视图的加载方式与我们的本地视图相同:

加载远程视图时,来自<head>元素的样式/脚本(如果有)将不会被评估。
类型
可能会有需要导航到外部网址的用例,为此,只需将data-rel="external"属性添加到导航元素中。请记住,这将通过卸载您的应用在浏览器上加载外部网址,并且您将由您的设备或浏览器的后退按钮来控制返回到您的应用。
后退按钮
后退按钮是移动应用中使用最广泛的用户界面元素之一,通常被添加到顶部导航栏中。剑道 UI Mobile 为我们提供了一个非常好用的后退按钮小部件;只要在anchor标签上加上data-role="backbutton",我们的后退按钮就准备好了!剑道 UI Mobile 自动处理首页导航,内置后退按钮小部件。
将后退按钮的标记添加到我们的应用的导航栏小部件中,将在我们到目前为止构建的所有三个视图的左上角显示后退按钮:
<header data-role="header">
<div data-role="navbar">
<a data-align="left" data-role="backbutton">Back</a>
<span data-role="view-title">Movie Tickets</span>
</div>
</header>

现在我们有一个问题要解决。后退按钮显示在所有视图中,包括主视图,这不是必需的。jQuery Mobile 等一些移动开发框架会在主屏幕上自动隐藏后退按钮,但在剑道 UI Mobile 中,我们将不得不自己处理。
同样,这很容易实现。我们需要更新主视图定义并添加一个 CSS 类:
<div data-role="view" id="mt-home-main-view" data-show="homeViewInit" data-layout="mt-main-layout" class="no-backbutton">
Home Page View
</div>
让我们将这个 CSS 片段添加到<head >标签中:
<style>
.no-backbutton .km-back { visibility: hidden; }
</style>
现在如果我们运行应用,我们可以看到主页上看不到后退按钮。渲染后的后退按钮将有一个由剑道用户界面手机自动添加的 CSS 类km-back。前面的 CSS 片段将使用km-back CSS 类隐藏元素,而使用no-backbuttonCSS 类隐藏元素,这是我们的主视图。
查看渲染的 HTML
这里需要注意的一个有趣的事实是,布局标题呈现在视图 div 内部,视图 div 中的其余内容显示在标题之后,内容元素内部,然后添加页脚元素。
渲染的 HTML 可以使用 Chrome/Safari 开发工具进行研究:

查看加载和 HTML 元素标识
现在我们已经很容易创建多个视图并相互导航了,重要的是要理解视图是如何加载到 DOM 中并显示的,以及它如何与视图中的 HTML 元素标识相关联。
让我们在示例应用中考虑以下导航场景:

用户加载应用,查看主页,导航到预告片屏幕,然后导航回主页。
当用户进行前面提到的导航时,DOM 中会发生这种情况:
- 主页的布局和视图已加载。
- 当用户导航到预告片视图时,使用
style="z-index: 0; display: none;"使主页的视图不可见。现在预告片屏幕的视图被加载到 DOM 中并变得可见。然后视图的z-index被设置为1。 - 现在,当用户导航到主页屏幕时,拖车屏幕的视图不可见,主页屏幕的视图可见,
z-index设置为1。
需要注意的重要点是,视图一旦加载到 DOM 中,就永远不会被移除;它只是在用户每次访问视图时变得可见和不可见。
在用户完成前面提到的导航之后,让我们检查应用的 DOM:

我们可以看到首页屏幕视图的z-index设置为1,关于屏幕视图不可见(该视图按照index.html中的定义加载),而预告片视图不可见z-index设置为0。
类型
由于视图一旦被初始化,就会一直保留在 DOM 中,并且变得可见和不可见,所以程序员必须小心命名视图中的 HTML 元素。如果两个视图具有相同标识的元素,并且如果任何客户端代码试图使用相同标识访问其中一个元素,可能会导致意想不到的后果。因此,建议对标识有适当的命名约定,这样当多个程序员在代码库上工作时就不会产生冲突。在视图名称的所有元素标识前加上三四个字符是避免麻烦的一种方法!
应用对象
之前我们已经看到了如何初始化移动应用和设置过渡配置选项。转换只是我们在实例化剑道用户界面移动应用时可以执行的许多全局配置中的一个例子。让我们探索一下使用应用对象可以实现的一些功能。
初始视图
使用初始配置选项,我们可以设置应用初始化后要显示的初始视图:
new kendo.mobile.Application($(document.body), {
initial: "intialViewID"
});
加载文本
使用加载配置,我们可以设置显示加载动画时显示的文本。如果该值设置为false,将不显示加载动画。文字需要包裹在< h1 > < / h1 >标签中:
new kendo.mobile.Application($(document.body), {
loading: "<h1>Loading...</h1>"
});
强制平台
正如我们所知,剑道渲染平台特定的用户界面。但是,它提供了使用平台配置选项在所有平台上强制特定平台的外观和感觉的选项:
<script>
new kendo.mobile.Application($(document.body), {
platform: "android"
});
</script>
即使该选项可用,使用平面用户界面主题也是构建跨平台一致外观和感觉的应用的推荐方式。
隐藏和显示加载动画
应用对象有助于以编程方式显示和隐藏加载动画。应用对象的showLoading()方法显示你的剑道手机应用的内置加载动画:
var application = new kendo.mobile.Application();
application.showLoading();
使用 Application 对象的hideLoading()方法,我们可以隐藏加载动画。
var application = new kendo.mobile.Application();
application.hideLoading();
获取当前视图的引用
当我们开发一个功能齐全的应用时,通常我们会遇到这样一个场景,我们需要在我们的 JavaScript 代码中获取当前显示视图的引用。
应用对象的view()方法提供当前显示的视图对象的引用:
var application = new kendo.mobile.Application();
//writes title of the displayed view to the console.console.log(application.view().title);
导航到视图
在前面的例子中,我们看到了如何以声明的方式导航到其他视图。但是如果你想用 JavaScript 导航到一个屏幕呢?对此也有一个解决方案。我们在第一个示例中创建的 Application 对象有一个navigate()方法,可以使用 JavaScript 代码在视图之间执行导航。该方法采用两个参数:要导航的网址和过渡效果(可选)。
这就是我们初始化剑道用户界面移动应用的方式:
var app = new kendo.mobile.Application();
现在我们可以利用这个对象应用调用navigate(url, transition)方法导航到另一个屏幕:
app.navigate("Trailers.html")
您甚至可以使用:
app.navigate("#mt-about-view", "slide")
类型
navigate("#:back")将带您回到之前访问过的视图。
跨平台的 UI 体验
我们已经为示例应用构建了框架,并看到了该应用在 iOS 上的外观。在第 1 章、使用 HTML5 构建移动应用中,我们已经讨论了 Kendo 针对特定平台的外观和感觉的设计理念。现在让我们来探索安卓体验和平面用户界面主题,它在所有移动平台上提供了统一的外观和感觉。
让我们通过在 Ripple 模拟器中将设备更改为 Nexus One 来体验一下安卓的外观和感觉:

如你所见,当应用在安卓平台上运行时,应用的外观和感觉会自动改变。符合安卓应用设计理念,将 TabStrip 小部件移到顶部,底部显示后退按钮。
这种自动的外观是通过将特定于平台的 CSS 类附加到呈现的 HTML 上来实现的。在安卓平台查看应用时,会将km-android等安卓特有的 CSS 类注入到<body>标签中,这样< body >内部的元素就会用专为安卓观感定制的类进行修饰。
让我们通过在 Chrome 开发人员工具的源代码视图中查看我们的应用来了解这一点:

在 iPhone 5 上查看时,同一个应用会附带一个特定于 iOS 的 CSS:

平面图
剑道提供自动的平台特定外观和感觉;很好!但是如果您希望在所有平台上都有一致的外观和感觉呢?剑道在 2013 年 Q2 发布时也回答了这个问题!剑道用户界面手机现在附带了一个平面用户界面主题,这是围绕 iOS 7 用户界面设计理念量身定制的。它提供了跨所有平台的统一外观和感觉。一旦启用了扁平皮肤,您就只有一组包装在km-flat类中的 CSS 来处理定制。这款皮肤对于那些拥有与其品牌相关的颜色的企业以及拥有自己商标外观的应用尤其有用。
初始化移动应用时,可以通过简单的配置skin:'flat'启用平面用户界面主题:
var application = new kendo.mobile.Application(document.body,
{
transition: 'slide',
skin:'flat'
});
这是我们的应用在启用了平面主题的 iPhone 5 和 Nexus One 手机上的外观:

总结
在本章中,我们看到了如何安装和使用 Ripple Emulator,以及在使用 Kendo UI Mobile 构建移动应用时,它对 web 开发人员有多有用。然后,我们创建了我们的第一个移动应用,并开始构建我们的示例电影票应用,同时学习剑道用户界面移动的内部。我们还探索了特定于平台的用户界面呈现和新的平面用户界面主题。
在下一章中,我们将学习如何使用微软的 ASP.NET 网络应用编程接口来构建 RESTful 服务。如果你不是.NET 程序员,您可以跳过下一章,使用本书网站上托管的 HTTP 服务。
三、使用 ASP.NET 网络应用编程接口的服务层
本章将通过示例介绍 ASP.NET 网络应用编程接口,并为我们的电影票应用创建服务层结构。如果您不是. NET 程序员,或者您不想弄乱数据和服务层,请随意跳过这一章。我们在本章中构建的服务是在线提供的,可以在构建示例应用时使用。该服务实例可以用作后端,同时遵循电影票应用集成。本章并不是对 ASP.NET 网络应用编程接口的完整参考,而是详细介绍了它的特性以及如何使用它来为我们的移动客户端开发、服务后端。需要了解 ASP.NET MVC,但不必遵循本章的内容。
在本章中,我们将介绍:
- 创建网络应用编程接口服务
- 选择途径
- 参数绑定
- 为电影票应用构建服务
- 内容协商
- 保护网络应用编程接口
ASP.NET Web API(原名 WCF Web API)是一个通过 HTTP 轻松构建面向资源服务的框架,可以使用 HTTP 的全部功能,可以被包括浏览器、控制台应用、服务、移动设备等在内的各种各样的客户端消费。它是在上构建 RESTful 应用的理想平台.NET 框架。网络应用编程接口是作为 ASP.NET MVC 4 的一部分提供的,它可以在 Visual Studio 2012 中获得,也可以作为 Visual Studio 2010 Service Pack 1 的附加组件。
ASP.NET 网络应用编程接口将 HTTP 作为应用级协议,利用 HTTP 的概念,并提供以下特性:
- 控制器中内置的动作映射,基于 HTTP 动词(GET、POST、PUT 等)
- 内容协商:使用该功能,服务根据客户端的请求决定响应的格式
- 请求/响应主体可以由任何类型的内容组成,例如二进制文件(图像/音频/视频...),HTML,JSON 等等;而不仅仅是 SOAP 所要求的 XML 内容
- 主机独立:既可以在 ASP.NET 内部托管,也可以在我们自己的主机进程中自托管
创建网络应用编程接口服务
现在让我们打开 Visual Studio 2012,用 C#为我们的电影票应用创建 ASP.NET 网络应用编程接口后端服务。
注
请注意,我们在本章中构建的服务层可通过以下网址在线获得:
http://API . kenomoble book . com/API/
如果您不想在本地创建服务,这里提供的服务方法可以用于测试和与您的应用集成。
现在,执行以下步骤来创建网络应用编程接口服务:
-
Make sure that you have installed Visual Studio 2012/2010 with MVC 4. If not installed, you can use the Microsoft Web Platform Installer to install them from the following link:
-
Launch Visual Studio, create a new project by navigating to the File menu and then New | Project.... Now from the pop up menu, select Visual C# Template from the left menu and select the ASP.NET MVC 4 Web Application template. Name the project as
MovieTickets.WebAPIand click on OK.![Creating a Web API service]()
-
In the project pop-up window, as shown in the following, select the Web API template from the template list. If you are interested in writing unit tests for your Web API services, you can check the Create a unit test project checkbox and Visual Studio will automatically create a test project for you.
您可以从列表中选择任何一个模板,因为它们都将包含创建网络应用编程接口控制器所需的动态链接库文件和配置。我们选择了网络T2 应用编程接口模板,因为这是唯一带有样本网络应用编程接口控制器的模板。
![Creating a Web API service]()
-
我们的 ASP.NET 网络应用编程接口服务现在由 Visual Studio 生成,带有大量与 MVC 4 相关的文件夹(内容、图像、脚本、视图等),这些文件夹可以从项目中删除,因为我们将仅将此解决方案视为 ASP.NET 网络应用编程接口服务。
在控制器文件夹中,生成两个控制器:HomeController.cs和ValuesController.cs。第一个控制器名叫HomeController.cs,是一个 MVC 4 控制器,ValuesController.cs是一个 Web API 控制器。与其他 MVC 4 文件和文件夹一起,HomeController.cs文件也可以从项目中移除。我们目前唯一感兴趣的文件是ValuesController.cs,它包含ValuesController类,是一个 Web API 控制器,继承自基类ApiController,不像 MVC 控制器是从Controller基类派生出来的。

如果我们通过点击打开ValuesController.cs,我们可以看到 Visual Studio 已经生成了与 HTTP 动词GET、POST、PUT和DELETE相匹配的方法,它们是 HTTP 请求执行的典型操作。
使用 IIS 托管
现在我们已经创建了我们的服务,让我们看看它是如何运行的,是使用 ASP.NET 运行时托管的。在本地 IIS 中我们的服务所在的文件夹上创建一个应用(如果您安装了 IIS Express ,您也可以使用它),以便可以使用您机器上的以下网址访问该服务:
http://localhost/movietickets.webapi
根据计算机上 IIS 的版本,执行此操作的步骤可能会有所不同。在 IIS 7 上,您可以使用以下步骤配置新应用:
-
通过在 Visual Studio 中转到构建 | 构建解决方案来构建解决方案。
-
点击窗口启动按钮,在运行文本框中,输入
inetmgr并点击进入。 -
打开 IIS,右键点击默认网站。
-
Select Add Application... as shown in the following screenshot:
![Hosting using IIS]()
-
在别名字段中输入
MovieTickets.WebAPI。 -
In the Physical path field, provide the physical path of the root directory of the project we created earlier.
![Hosting using IIS]()
-
点击确定,我们的 Web API 服务就可以使用了。
要测试是否一切正常,打开你最喜欢的浏览器,点击地址栏中的以下网址:
http://localhost/movietickets.webapi/api/values
浏览器上会显示一个带有数据value1和value2的 XML 文件,数据是通过ValuesController文件中的GET方法返回的,如下图截图所示:

类型
不要忘记在网址中添加/api/,否则会出现 404 错误。这里api只是作为避免同一个项目中 MVC 和 Web API 控制器之间路由冲突的一种便捷方式。您可以在下一节看到这是如何工作的。
路由
路由、简单来说就是接收请求并将其映射到控制器动作的过程。如果你熟悉 MVC 路由,你会发现 Web API 路由和它类似,但有一些区别;Web API 使用 HTTP 请求类型而不是 URL 中的动作名称(如 MVC 的情况)来选择要在控制器中执行的动作。
网络应用编程接口的路由配置在文件App_Start\WebApiConfig.cs中定义。ASP.NET 网络应用编程接口的默认路由配置如下所示:
namespace MovieTickets.WebAPI
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
Web API 的默认路由模板是api/{controller}/{id},如前面代码中突出显示的。
如果我们在文件App_Start\RouteConfig.cs中打开 MVC 的默认路由配置,我们可以看到默认路由定义是作为{controller}/{action}/{id}提供的。
namespace MovieTickets.WebAPI
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
);
}
}
}
两种路由定义可以注意到的一个明显区别是,在 Web API 路由定义中,缺少{action}路由值。这是因为在网络应用编程接口中,HTTP 动词用于定位动作,因此 URI 不需要提供动作名称。
例如,当网络应用编程接口接收到对特定控制器的GET请求时,匹配动作的列表都是名称以GET开头的方法(例如,GetMovies、get 加热器等)。所有其他 HTTP 动词也是如此。通过匹配方法的签名来识别要调用的正确操作方法。
可以通过使用动作方法的HttpGet、HttpPut、HttpPost或HttpDelete属性来覆盖动作方法的命名约定。例如,我们可以将动作方法Get(int id)重命名为MyValues(int id),并用HttpGet属性进行修饰:
[HttpGet]
public string MyValues(int id)
{
return "my value";
}
现在我们用 URL http://localhost/movietickets.webapi/api/value/1打浏览器,可以看到字符串my value会被返回。如果属性HttpGet从动作中移除,网络应用编程接口将不会将方法MyValues识别为动作方法。
定制网络应用编程接口路由
默认的网络应用编程接口路由适用于理想的场景,在这种场景中,每个 HTTP 动词只定义了几个动作方法,它们只在方法签名上有所不同。根据您的项目需求,您最终可能需要多个GET方法或多个POST方法,这些方法具有不同的功能和名称,调用这些方法的应用需要通过它们的名称来调用它们,而不是允许网络应用编程接口使用 HTTP 动词来计算方法名称。
让我们通过在我们的ValuesController中添加另一个名为GetMyValues的动作方法来看看这个动作场景:
public string GetMyValues()
{
return "Get my values";
}
现在的要求是,我们需要调用这个特定的操作方法,让我们通过使用这个 URL 调用这个方法来尝试这样做:
http://localhost/movietickets.webapi/api/values/GetMyValues
结果将是一条错误消息,如下图所示:

消息称找到了多个与请求匹配的操作:,这是因为网络应用编程接口路由识别了两个与该请求匹配的操作方法;Get和GetMyValues。
为了解决这个问题,我们需要在WebApiConfig类中添加以下路由配置,该类在当前配置之上使用基于动作名称的路由:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi2",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
确保您将此路由配置添加到了Web API 默认配置之上,否则默认路由将优先,并且会发现多个操作...错误将再次显示。
一旦添加了前面的代码,我们点击了网址
http://localhost/movietickets.webapi/api/values/Getmyvalues再次在浏览器上,我们可以看到新的结果:

参数绑定
参数绑定 就是将一个 HTTP 请求中的内容转换为.NET 对象,以便可以向操作方法参数提供值。如果没有参数绑定支持,开发人员将不得不在操作方法中编写大量容易出错且乏味的代码,以从原始 HTTP 请求中检索参数值。
在 URI,输入参数通常作为查询字符串嵌入到 HTTP 请求中,或者嵌入到请求的正文中。Web API 使用名为模型绑定 的技术从查询字符串中读取参数值,并使用格式化程序 从请求体中读取。
模型绑定
如果你熟悉 ASP.NET MVC,那么你会发现它和网络应用编程接口中使用的模型绑定概念是一样的,用于从用application/form-url-encoded编码的查询字符串、标题或正文中读取值。当遇到请求时,在ValueProviderFactories类中注册的值提供者从请求中提取数据,并将它们提供给模型绑定器,模型绑定器使用这些值创建模型对象。
默认情况下,简单类型如s tring、Date Time、Guid、decimal和其他用类型转换器 装饰的类型使用模型绑定。
[ModelBinder]属性可用于参数或参数类型,以强制模型绑定。源自[ModelBinder]的[FromUri]属性告诉模型绑定器只在 URI 寻找值。
格式化程序
网络应用编程接口使用格式化程序从参数绑定的 HTTP 请求体中读取数据。网络应用编程接口中引入了格式化程序,以提供更好的内容协商能力。内容协商是一种机制,它允许 web 服务器根据客户端的请求,使用相同的 URL 以不同的格式提供内容,这将在本章后面讨论。
媒体格式化程序源自MediaTypeFormatter类。所有配置的格式化程序都可以在HttpConfiguration.Formatters集合中找到。网络应用编程接口使用传入请求的内容类型从该集合中标识要使用的正确格式化程序。
ASP.NET 网络应用编程接口中的请求正文内容被视为只读、一次读取和非缓冲流。这意味着:
- 我们在动作方法签名中只能有一个
complex类型。如果需要一个以上的complex类型,除了一个以外的所有其他类型都必须使用[ModelBinder]属性进行装饰。 - 如果格式化程序用于从请求体中读取参数值,那么在操作方法中将无法再访问请求体。
这与 MVC 的参数绑定有很大的不同,在 MVC 中,请求体被缓冲,以便可以多次搜索参数值。
网络应用编程接口为 XML 和 JSON 提供现成的格式化程序,开发人员可以创建自定义格式化程序来支持其他媒体类型。
类型
我们可以使用[FromBody]属性来指定参数应该从正文中读取,而不是从 URI 中读取。该属性在简单类型作为POST请求的参数发送的情况下非常有用。默认情况下,网络应用编程接口将在 URI 寻找一个simple类型,由于请求是一个POST,该值将在请求的正文中。
在ValuesController中,Post动作方法有【FromBody]属性的实现:
public void Post([FromBody]string value)
{
}
为电影票应用构建服务
现在我们已经介绍了 ASP.NET 网络应用编程接口的一些基础知识,让我们开始为我们的电影票应用构建后端服务。由于这本书不是关于数据库或访问数据的,我们遵循的方法是对服务在存储库类中返回的数据进行硬编码(对象的数据是在实例化对象时添加的),而不是从数据库中读取数据。
让我们来看一下我们服务架构的高级视图:

服务架构被简化了,这样我们就不会花很多时间在书的上下文之外的主题上,比如数据库设计、数据访问等等。
来自客户端移动应用的请求将由 ASP.NET 网络应用接口请求管道接收,并将调用控制器中的一个操作方法。动作方法将依次调用业务层 获取或更新数据,业务层依次调用存储库类 返回硬编码数据 。在本章中,我们将只讨论创建一个控制器和操作方法。示例应用和所有其他控制器和方法可以在 Packt 网站上的代码包中找到,文件夹名为chapter 3。这种方法有助于避免没有新信息的冗余代码,并帮助我们专注于书中的重要主题。
按照以下步骤设置电影列表的数据,这些数据将显示在我们的“电影票”应用的主页上:
-
在
BLL\BusinessObjects文件夹中创建一个名为MovieBO.cs的文件,并创建一个名为MovieBO的类,如下代码所示:public class MovieBO { public int MovieId { get; set; } public string Name { get; set; } public string Rating { get; set; } public string Length { get; set; } public string Genre { get; set; } public string LeadStars { get; set; } public string Image { get; set; } public bool IsNowPlaying { get; set; } } -
Create a class called
MovieRepositoryin the fileBLL\ MovieRepository.cs, which is responsible for generating data which our service will return to the client. After adding the new class, add a class-levelpublic staticvariable calledmoviesMasterList:public static List<MovieBO> moviesMasterList;这个变量将保存一个电影的主列表,业务逻辑将查询这个集合来选择匹配它的查询。
-
现在,让我们添加一个名为
CreateMoviesMasterList的方法,为moviesMasterList变量添加几部电影:private static void CreateMoviesMasterList() { moviesMasterList = new List<MovieBO>() { new MovieBO(){ MovieId = 1, Name = "The Great Gatsby (2013)", Genre = "Drama", Image = "http://images.kendomobilebook.com/movies/greatgatsby.jpg", IsNowPlaying = true, LeadStars = "Leonardo DiCaprio, Amitabh Bachchan", Length = "143", Rating = "PG-13" }, new MovieBO(){ MovieId = 2, Name= "Iron man 3", Genre= "Sci-Fi", Image = "http://images.kendomobilebook.com/movies/ironman3.jpg", IsNowPlaying = true, LeadStars = "Robert Downey Jr., Gwyneth Paltrow", Length = "130", Rating = "PG-13" } }; } -
现在创建一个名为
GetMoviesMasterList的公共静态方法来返回电影主列表:public static List<MovieBO> GetMoviesMasterList() { if (moviesMasterList == null || moviesMasterList.Count == 0) { CreateMoviesMasterList(); } return moviesMasterList; } -
为了分层架构的,让我们创建一个业务层,它只不过是
BLL\ MovieTicketsBLL.cs文件中一个名为MovieTicketsBLL的类,它将包含访问存储库并将业务对象返回给控制器的静态方法。让我们添加一个名为GetMovies的方法,它将根据提供的搜索字符串返回电影。如果搜索字符串为空,则返回整个电影列表:public static List<MovieBO> GetMovies(string searchKeyword) { var moviesMasterList = MovieRepository.GetMoviesMasterList(); if (!string.IsNullOrEmpty(searchKeyword)) { return (from m in moviesMasterList where m.Name.StartsWith(searchKeyword, StringComparison.CurrentCultureIgnoreCase) select m ).ToList(); }else { return moviesMasterList; } }
添加控制器
现在,我们需要创建一个名为MovieController的 Web API 控制器,其中包含了与电影相关的动作方法。
右键单击控制器文件夹,选择添加,然后选择控制器...。在弹出的添加控制器中,从模板下拉菜单中选择读写动作为空的 API 控制器,命名为MoviesController。确保不选择任何 MVC 控制器模板,只选择 API 控制器模板。

控制器文件生成后,在GET方法中,添加以下代码:
public List<MovieBO> Get(string id)
{
return MovieTicketsBLL.GetMovies(id);
}
public List<MovieBO> Get()
{
return MovieTicketsBLL.GetMovies("");
}
如果有随请求一起传递的要搜索的关键字,将调用第一个GET方法。此操作方法将从搜索关键字开始检索所有电影。如果没有输入参数,则调用第二个GET方法,返回整个电影列表。添加代码后,一定要引用定义业务对象和业务层的命名空间。
您现在准备测试您的网络应用编程接口方法,该方法将根据您的搜索标准返回电影列表。
这里显示的是用于调用网络应用编程接口服务的网址以及在浏览器上看到的结果:
-
To filter by movie names, insert the following URL in your browser address bar and hit Enter:
http://localhost/movietickets.webapi/api/movies/get/i![Adding a controller]()
-
To see the entire movies list use the following URL and hit Enter:
http://localhost/movietickets.webapi/api/movies/![Adding a controller]()
内容协商
您现在应该已经注意到我们所有的例子都有来自 XML 格式的网络应用编程接口服务的数据。通常,在构建移动网络应用时,JSON 是首选格式,有时我们可能需要使用这些格式或一些其他自定义格式。得益于 Web API 的内容协商功能、客户端可以告诉 Web API 服务它接受什么内容格式,Web API 可以自动为相同格式的客户端服务;前提是格式是在 Web API 中配置的。它只支持现成的 XML 和 JSON 格式。
由于以下原因,JSON 是目前首选的数据交换格式:
- JSON 是轻量级的,带宽不密集。
- JavaScript 可以直接消费 JSON 并解析为
JS对象。不需要格式转换。 - 对基于 JSON 的水平可扩展数据库的兴趣越来越大,如 MongoDB、CouchDB 等。
- JSON 几乎可以与任何编程语言互操作,因为它仅限于原始数据类型。
注
W3C 在其 HTTP 规范中将内容协商定义为:当有多种表示可用时,为给定响应选择适当表示的机制。例如见以下链接:http://www.w3.org/Protocols/rfc2616/rfc2616.txt
网络应用编程接口使用Accept、Content-Type和Accept-Charset HTTP 头进行内容协商。
使用我们在上一节中构建的MoviesController文件,让我们看看这是如何操作的。
高级休息客户端 Chrome 扩展
高级休息客户端 是可用于 chrome 浏览器的扩展,用于创建和测试自定义 HTTP 请求。它支持诸如 JSON/XML 响应查看器、套接字调试、设置自定义头等功能。我们将使用这个简单的轻量级工具来探索网络应用编程接口的内容协商特性。
可以通过以下链接安装在你的 Chrome 浏览器上:
如果您不介意使用网络调试工具,如 Fiddler ,请随意使用它,因为我们正在尝试创建一些自定义的 HTTP 请求并比较响应。
接受标题
HTTP Accept 头用于指定响应中客户端可接受的媒体类型。对于 XML,该值设置为application/xml,对于 JSON,该值为application/json,并指定接受所有媒体类型【使用 T2】。
要看到这一点,让我们打开高级休息客户端扩展,向网址http://localhost/movietickets.webapi/api/movies/get/i发送一个 GET 请求,并将添加新标题字段设置为接受标题为application/json。

我们的网络应用编程接口服务的响应可以在下面的截图中看到:

如果您将接受头的值更改为application/xml,响应将由网络应用编程接口服务以 XML 形式发送,如浏览器窗口中所示。
内容类型标题
请求中的Content-Type头指定请求正文的 MIME 类型。当设置了Content-Type头(用于POST和PUT请求)并且没有设置接受头时,网络应用编程接口将使用Content-Type头来决定响应的媒体类型。
如果设置了Accept和Content-Type头,则Accept头中指定的媒体类型用于确定响应的媒体类型。在这种情况下,如果网络应用编程接口中没有Accept头中定义的媒体类型的格式化程序,将选择Content-Type头来确定响应媒体类型。
接受字符集标题
Accept-Charset标头用于指示响应的字符编码,不同于Accept和Content-Type标头,后者指定响应的 MIME 类型。
网络应用编程接口使用 UTF 8 和 UTF-16 开箱即用的字符编码,默认使用UTF-8。在请求头中设置Accept-Charset: utf-16将返回UTF-16编码的响应。要基于每个格式化程序在服务器端自定义字符编码,您需要更改MediaTypeFormatter类的SupportEncodings属性。
类型
如果请求包含一个 X-Requested-With 头,表示一个 Ajax 请求,如果没有指定 Accepted 头,服务器可能默认为 JSON。
注
如前所述,默认情况下,网络应用编程接口只支持 XML 和 JSON 媒体格式化程序。为了支持其他媒体类型,开发人员需要通过从MediaTypeFormatter(异步读/写)类或BufferedMediaTypeFormatter(同步读/写)类派生来创建自定义格式化程序。由于自定义格式化程序的创建不在本书的讨论范围之内,因此我们在此不再详细讨论。如果您有兴趣探索自定义格式化程序,Mike Wasson 的这篇文章将是一个很好的起点:
作为响应的图像/PDF 文件
在大多数应用中,检索 PDF 或图像文件作为响应是一个常见的要求。通过在您的操作方法中编写几行代码来覆盖由内容协商确定的响应的默认媒体类型,在网络应用编程接口中实现这个场景是非常容易的。
让我们打开ValuesController.cs文件,再添加一个名为GetImage()的动作方法,它会在服务的Content文件夹中返回一个图像文件,如图所示:
public HttpResponseMessage GetImage()
{
byte[] bytes = System.IO.File.ReadAllBytes(
HttpContext.Current.Server
.MapPath("~/Content/Kendo.png"));
var result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentType
= new MediaTypeHeaderValue("img/png");
return result;
}
也请参考控制器中的这两个名称空间:
System.Web;
System.Web.Http;
这个实现的关键是使用HttpResponseMessage类,这有助于在控制器本身中创建一个原始的 HTTP 响应。然后,我们使用枚举HttpStatusCode.OK设置 HTTP 200 状态代码,并将图像字节设置为响应内容。最后一步是将媒体类型头设置为img/png,回复为HttpResponseMessage类型。
现在点击浏览器上的网址http://localhost/movietickets.webapi/api/values/GetImage,这个动作方法会显示优雅的剑道 UI 标志(如果你使用了源代码附带的图片!).

如果内容是 PDF 文件,我们唯一需要做的改变就是将 MIME 类型更新为application/pdf。
保护网络应用编程接口
在真实的- 世界场景中,发布的大多数服务都必须是安全的,只有经过身份验证的客户端才应该能够访问这些服务。默认情况下,在几乎所有企业场景中使用 SSL 来实现传输层安全性,以防止窃听通过网络传输的数据。在本节中,让我们通过实现认证和授权来关注应用级安全性。
身份验证是确定用户是他声称的那个人的过程,而授权是验证经过身份验证的用户是否可以执行特定的操作或使用特定的资源。
认证
通过身份验证,我们试图实现的是确保 Web API 服务接收到的每个请求都是用正确的凭据从客户端发送的。实现身份验证有不同的方式,如基本、摘要等。我们将讨论基本身份验证,其中客户端为每个请求在 HTTP 头中发送一个 Base64 编码的用户名和密码。一旦您了解了如何在网络应用编程接口中实现基本身份验证,就很容易挂钩其他形式的身份验证,因为只有身份验证过程不同,而网络应用编程接口挂钩(在哪里进行)也是一样的。
基本认证
基本身份验证,顾名思义,是对 HTTP 请求进行身份验证的最简单、最基本的形式。它不需要服务器端的会话存储或 cookies 的实现。
下图显示了基本身份验证的实现:

使用消息处理程序进行身份验证
网络应用编程接口希望它的主机(在我们的例子中,是 IIS)对请求进行身份验证。为此,我们可能必须使用任何 ASP.NET 身份验证模块,或者编写一个 HTTP 模块来实现身份验证。
要验证传入请求,主机需要创建一个主体,它是一个类型为IPrincipal的对象,代表当前的安全上下文。然后主机需要通过设置Thread.CurrentPrincipal将这个主体对象附加到当前线程。与主体关联的身份对象有一个名为IsAuthenticated的属性。如果用户通过认证,该属性将返回true;否则它将返回false。由于我们在示例项目中进行网络托管,我们也需要设置HttpContext.Current.User,以使安全上下文一致。
通常,出于各种原因,将身份验证保持在服务之外不是开发人员所希望的。为了在我们的服务中保留身份验证部分,我们需要利用网络应用编程接口消息处理程序。消息处理程序源自抽象类HttpMessageHandler。他们负责接收 HTTP 请求和发送响应。我们可以通过从System.Net.Http.DelegatingHandler派生来创建自定义消息处理程序,并添加我们自己的原始 HTTP 数据操作代码。源自DelegatingHandler有助于通过调用base.SendAsync调用管道中的内部消息处理程序。在消息处理程序中,请求作为HttpRequestMessage可用,响应作为HttpResponseMessage可用。使用处理程序时,可以使用HttpRequestMessage对象轻松完成之前的认证过程。
这些是使用自定义消息处理程序进行基本身份验证所涉及的步骤:
- 使用消息处理程序捕获传入的请求
- 如果请求包含授权头:
- 尝试对用户进行身份验证,如果身份验证成功,请设置主体并允许继续执行
- 如果认证失败,设置 HTTP 状态码 401,并在响应头中返回 WWW-Authenticate: Basic
类型
WWW-Authenticate: Basic 头用于表示该服务使用的身份验证方案为 Basic。
实施认证
让我们编写一些代码并为我们的电影票网络应用编程接口项目设置身份验证模块:
-
创建一个名为
Common的文件夹,添加一个名为MovieTicketsPrincipal的类,并添加如下代码:using System.Security.Principal; namespace MovieTickets.WebAPI.Common { public class MovieTicketsPrincipal: IPrincipal { public string UserName { get; set; } public IIdentity Identity { get; set; } public bool IsInRole(string role) { if (role.Equals("user")) { return true; } else { return false; } } //Constructor public MovieTicketsPrincipal(string userName) { UserName = userName; Identity = new GenericIdentity(userName); } } } -
Create a folder called
Handlersin the Movie Tickets Web API project, add a class calledAuthMessagehandler, and add the following code:using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Web; using MovieTickets.WebAPI.Common; namespace MovieTickets.WebAPI.Handlers { public class AuthMessagehandler : DelegatingHandler { private string _userName; //Capturing the incoming request by overriding //the SendAsync method protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { //if the credentials are validated, //set CurrentPrincipal and Current.User if (ValidateCredentials( request.Headers.Authorization)) { Thread.CurrentPrincipal = new MovieTicketsPrincipal(_userName); HttpContext.Current.User = new MovieTicketsPrincipal(_userName); } //Execute base.SendAsync to execute default //actions and once it is completed, //capture the response object and add //WWW-Authenticate header if the request //was marked as unauthorized. return base.SendAsync(request, cancellationToken) .ContinueWith(task => { HttpResponseMessage response = task.Result; if (response.StatusCode == HttpStatusCode.Unauthorized && !response.Headers .Contains("WWW-;Authenticate")) { response.Headers .Add("WWW-Authenticate", "Basic"); } return response; }); } //Method to validate credentials from Authorization //header value private bool ValidateCredentials( AuthenticationHeaderValue authenticationHeaderVal) { if ( authenticationHeaderVal!= null && !String.IsNullOrEmpty(authenticationHeaderVal.Parameter)) { string[] decodedCredentials = Encoding.ASCII.GetString( Convert.FromBase64String( authenticationHeaderVal.Parameter)) .Split(new[] { ':' }); //now decodedCredentials[0] will contain //username and decodedCredentials[1] will //contain password.You need to implement your own //business logic to verify credentials here. //For simplicity, we are hardcoding username //and password here. if (decodedCredentials[0].Equals("username") && decodedCredentials[1].Equals("password")) { _userName = "John Doe"; return true;//request authenticated. } } return false;//request not authenticated. } } }在这个自定义消息处理程序中,我们使用以下代码覆盖
SendAsync方法来捕获传入的请求:protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { }一旦捕获到传入请求,就调用
ValidateCredentials()方法来检查包含 Base64 编码格式的用户名和密码的Authorization头值的 HTTP 头。如果找到标题,将提取并验证用户名和密码。在前面的代码中,为了简单起见,我们对照硬编码字符串username和password进行验证,但通常在现实示例中,将对照活动的目录或数据库检查凭据。如果发现凭证正确,则创建类型为MovieTicketsPrincipal的对象,并将其分配给Thread.CurrentPrincipal和HttpContext.Current.User。现在,从服务层的任何地方,都可以访问当前用户的详细信息以进行授权、审核等。一旦响应通过验证,就调用base.SendAsync向内部处理程序发送请求。内部处理程序异步处理请求并发回响应。以下代码确保内部代码仅在收到响应后执行:
base.SendAsync(request, cancellationToken) .ContinueWith(task =>{--- });如果响应包含一个未经授权的 HTTP 头,代码会注入一个值为
Basic的WWW-Authenticate头,通知客户端我们的服务需要基本身份验证。关于被标记为未授权的请求的更多细节将在授权部分讨论。 -
Now open
Global.asax.csand add the following code in theApplication_Startevent as the first line:GlobalConfiguration.Configuration .MessageHandlers.Add(new AuthMessagehandler());这段代码将把我们的
AuthMessagehandler类添加到MessageHandlers集合中,这样当请求到达时就会调用它。
正在进行认证
要查看我们的身份验证机制的运行情况,让我们使用高级 REST 客户端创建一个对 URL http://localhost/movietickets.webapi/api/movie的新请求,该请求带有标题授权:基本 dxnlcm5 hbwu 6 cgfzc3 vcmq =(其中“dxnlcm5 hbwu 6 cgfzc3 vcmq =”只不过是username:password的 Base64 编码形式),如下图所示:

现在让我们在SendAsync方法中添加几个断点,看看控制流程:

现在我们通过打开请求对象来检查参数和方案属性的值:

如您所见,所提供的凭据在消息处理程序中得到了正确验证:

现在让我们继续到下一部分,在那里认证数据将被用于动作方法的访问控制。
授权
到目前为止,我们创建的所有控制器动作方法都是不安全的,这意味着未经身份验证的用户可以访问它们。既然我们已经看到了如何对请求进行身份验证,那么让我们看看如何使用这些信息来授权对某些控制器或动作方法的访问。
网络应用编程接口中的授权过程发生在认证之后的管道中,并且在控制器动作被执行之前。这有助于根据经过身份验证的用户提供访问控制。
对于授权,网络应用编程接口使用授权过滤器,如果请求未通过身份验证,该过滤器将做出错误响应,并阻止受保护的操作方法的执行。
使用 AuthorizeAttribute
网络应用编程接口提供了一个名为AuthorizeAttribute的授权过滤器。该属性验证请求的IPrincipal,检查其Identity.IsAuthenticated属性,如果该值为假,则返回 401 未授权 HTTP 状态,请求的动作方法将不被执行。
AuthorizeAttribute属性可以应用于三个级别:
- 全球的
- 控制器
- 行动级别
全球层面
全局实现是通过在全局过滤器列表中添加AuthorizeAttribute来实现的。有关示例,请参见下面的代码片段:
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
一旦添加了这段代码,只允许经过身份验证的请求调用网络应用编程接口服务中的操作方法。
控制器级别
用[Authorize]属性装饰一个控制器将防止控制器中的所有动作方法被未授权的用户访问。
让我们创建一个名为AccountController的新控制器,并添加一个名为 RemoveUser 的动作方法来移除一个用户,然后用[ Authorize]属性修饰控制器:
[Authorize]
public class AccountController : ApiController
{
// GET api/account
[HttpPost]
public bool RemoveUser(int id)
{
//custom code to remove user
return true;
}
}
现在,让我们尝试调用RemoveUser操作方法,而不发送适当的凭据,让我们看看会发生什么:
-
打开高级休息客户端。
-
选择请求类型为
POST。 -
Hit the following URL:
http://localhost/movietickets.webapi/api/account/Removeuser/345此操作将在浏览器中显示如下内容:
![Controller level]()
如您所见,网络应用编程接口以 WWW-Authenticate 标题和以下消息响应了 401 未授权状态代码:
"Message": "Authorization has been denied for this request."
如果出现要求凭证的弹出窗口,只需点击取消。
当为这个请求发送了适当的凭证时(也就是说,通过在请求中添加Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=头),您将看到操作被执行并且true被返回。
动作级别
如果您希望在同一个控制器中既有受保护的动作又有不受保护的动作,可以用Authorize属性来修饰单个动作,以便它们需要经过身份验证的请求来调用它们,而它们的动作可以通过匿名请求来执行:
public class AccountController : ApiController
{
// GET api/account
[HttpPost]
[Authorize] //protected
public bool RemoveUser(int id){...}
//Unprotected action
public bool Login(UserBO user){...}
}
【允许匿名】属性
该属性标记控制器和动作,以便在授权期间跳过AuthorizeAttribute。如果控制器中有大量受保护动作,极少数不受保护,控制器可以用[Authorize]修饰,不受保护的动作可以用[AllowAnonymous]属性修饰。
因此,动作级授权中提到的代码可以这样编写:
[Authorize]
public class AccountController : ApiController
{
// GET api/account
[HttpPost]
public bool RemoveUser(int id){...}
[AllowAnonymous]
public bool Login(UserBO user){...}
}
角色和用户检查
也可以过滤某些角色和用户,以便只授予他们对操作方法的访问权限。
角色检查可以通过两种方式完成:
- 使用
User.IsInRole("Admin")内的动作方法代码。用户是当前主体,在ApiController基类中定义。 - 在控制器和动作上使用
[Authorize(Roles = "Admin")]。
类型
如果有多个角色,并且您需要检查用户是否处于任何角色,请使用[Authorize(Roles = "Admin, PowerUser")]。
同样,用户也可以过滤:
- 使用
User.Identity.Name.Equals("Bob")内的动作方法代码 - 在控制器和动作上使用
[Authorize(User = "Bob")]
自定义授权属性
在真实的- 世界应用中,使用复杂的逻辑来实现授权(例如,您希望防止某个范围的 IP 地址调用某些操作方法),内置的AuthorizationAttribute可能不够,我们可能不得不依赖于创建自己的自定义授权过滤器。这可以通过三种方式实现:
- 通过异步扩展
AuthorizeAttribute - 通过同步延伸
AuthorizationFilterAttribute - 通过异步实现
IAuthorizationFilter
让我们看看如何扩展AuthorizeAttribute属性并创建自定义授权属性:
public class AdminUsersOnlyAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext context)
{
var currentPrincipal = Thread.CurrentPrincipal;
// custom logic to check whether user is admin or not
}
protected override void HandleUnauthorizedRequest(
HttpActionContext actionContext)
{
// custom response for unauthorized request
}
}
现在控制器和动作方法可以用[AdminUsersOnly]属性修饰,防止非管理员用户访问。
为了降低本书的复杂性,我们将AuthorizationFilterAttribute 的扩展和的实现IAuthorizationFilter 留给你作为练习。
总结
在本章中,我们学习了 ASP.NET 网络应用编程接口的特性,以及如何创建和配置新的网络应用编程接口服务。我们还了解了网络应用编程接口如何实现路由、参数绑定、内容协商和安全性。对于示例电影票应用,我们创建了一个服务工作结构,其中包含了我们在本章中学习的所有功能。在下一章中,我们将探索剑道用户界面框架的元素,并将网络应用编程接口与我们的剑道用户界面移动应用相集成。
四、使用框架元素的集成
在本章中,我们将介绍一些重要的剑道框架元素,如数据源、模板、MVVM 等。在本章的后面,我们将讨论并查看如何通过构建电影票应用的用户帐户屏幕来将网络应用编程接口后端与剑道用户界面前端集成。我们还将讨论如何通过实现揭示模块模式来模块化我们的 JavaScript 代码,并看看如何 CORS 启用我们的网络应用编程接口服务。
我们将在本章中讨论以下主题:
- 数据源
- 模板
- 视图模型
- 与电影票应用集成
剑道用户界面的框架元素通过提供强大的组件将剑道用户界面与市场上所有其他 HTML5/JavaScript 框架区分开来,这为构建网络和移动应用奠定了坚实的基础。虽然大多数其他框架只是单独提供某些功能,如用户界面小部件、路由、模板等,但由于框架元素,剑道用户界面提供了“简单的一切”来构建端到端的网络和移动应用。
剑道的所有核心框架元素都可以在网络和移动应用中使用,没有任何限制。
数据源
对于程序员来说,用大量的 JavaScript 数据操作来开发屏幕是一个颇具挑战性的体验,比如过滤、排序、分页、分组、聚合等等。当涉及远程和本地数据源时,开发变得更加复杂。
Kendo 对这个问题的解决方案是 DataSource 框架元素,它通过提供命令来执行常见操作,并成为各种外部和本地数据源的单一接口,从而抽象出所有这些复杂性,让开发人员的生活变得轻松。它支持 CRUD 操作(创建、读取、更新和销毁)以及数据的过滤、排序、分页、分组和聚合。
简单的DataSource对象可以初始化为:
var kendoDS = new kendo.data.DataSource();
这段代码除了初始化一个没有任何数据的DataSource对象之外什么都不做。为了使这个对象有用,我们需要向该对象提供数据,或者告诉该对象可以从哪里获取数据。让我们看看将本地和远程数据馈送到数据源的一些选项。
本地数据源
有时开发人员会以 JavaScript 对象的形式在本地获得数据,他们需要将这些数据绑定到一个或多个小部件中,以便用户可以对其进行操作。将本地数据绑定到DataSource对象非常方便,因为大多数常见的操作都可以在数据上执行,而无需编写太多自定义代码。
注
从这一章开始,你会看到很多例子上传到jsfiddle.net。js 提琴 是一个在线 JavaScript 实时编码环境,它帮助开发人员执行 JS/HTML/CSS 代码,并使用他们的网络浏览器查看结果。一旦您在 jshutch 中打开了示例代码,您就可以点击 fork 按钮,以现有代码为基础创建您自己的代码版本。如果您是在线 JS IDEs 的新手,以下链接中提供的 JS 提琴教程将是一个很好的探索场所:
http://doc.jsfiddle.net/tutorial.html
让我们看看如何将本地 JavaScript 数组绑定到DataSource对象:
var videoGames = [{
name: "Need for Speed: Underground",
year: 2004,
copiesSold: "1.10 million"
}, {
name: "Halo: Combat Evolved",
year: 2001,
copiesSold: "5 million"
}, {
name: "Grand Theft Auto Double Pack",
year: 2003,
copiesSold: "1.70 million"
}];
var videoGamesDS = new kendo.data.DataSource({
data: videoGames
});
这很简单,只需在初始化时将本地 JavaScript 对象分配给data属性。
现在,让我们调用方法,该方法给出了alert()方法中DataSource的项目数,以查看我们的数据是否被正确加载,如代码片段所示:
alert(videoGamesDS.total());
警报将返回0,因为我们只初始化了DataSource,没有用数据填充DataSource对象。为了使数据在DataSource对象中可用,我们需要调用read()方法。read()方法从提供的来源读取DataSource对象内部的数据。用read()更新代码后,我们可以看到警报将显示 3 ,确认数据源中所有三个项目都可用:
videoGamesDS.read();
alert(videoGamesDS.total());

类型
试试 js 提琴:http://jsfiddle.net/kendomobile/6FfWs/
远程数据源
这是移动应用常见的场景,其中数据(通常是 JSON)需要使用提供的网址从远程服务获取。在这种情况下,我们需要在初始化DataSource的同时增加一些配置。以下是如何为使用 JSON 格式的远程服务初始化DataSource:
var remoteDataSource = new kendo.data.DataSource({
transport: {
read: {
// the remote url
url: "http://yourdomain.com/jsonservice",
// specify data format
dataType: "json",
// optional input parameters
data: {
inputParam: "inputParameterValue"
}
}
},
// describe result format
schema: {
// use data available in the "listOfItems" field
data: "listOfItems"
}
});
transport:指定加载和保存数据的设置。数据可以从远程服务、本地文件或内存数据中加载。read:读取数据的设置指定为:url:是远程数据/本地文件的 URLdataType:是用于通讯的数据格式data:是远程服务的输入参数
schema:定义接收到的原始数据的结构组织。在前面的代码中,我们指定字段listOfItems包含数据项。我们可以在schema配置中定义聚合、错误、总计、分组和解析的数据字段。
DataSource是剑道 UI 框架中一个非常强大的对象,我们刚刚对它进行了介绍。关于DataSource的完整讨论超出了本书的范围,因此我们强烈建议您使用以下网址中的剑道文档进一步探索数据源:
模板
模板 是一种简单方便的方法来构建复杂的 UI 结构,通常带有重复的块,代表您的视图模型。剑道 UI 在核心框架中提供了强大的模板引擎,针对高性能进行了优化。它简化了语法,使得只需要了解 JavaScript 就可以更容易地使用。
剑道用户界面的模板语法(称为哈希模板)使用#符号来识别模板中要插入数据的区域。它们可以通过以下三种方式使用:
-
#= lastName #:这会渲染变量lastName中存储的值。 -
#: address #: This renders the value with its HTML encoding. This is useful to prevent rendering issues with user-input fields. For example, if the value of the address is provided as:<div> 123, streetname, state </div>然后屏幕上呈现的 HTML 将显示:
<div> 123, streetname, state </div> -
#for(... ){# ... #}#:这将执行模板中的 JavaScript 代码,以便根据特定条件(如循环)进行渲染。模板#for(i=0;i<3; i++){#value of i: #=i# <br/> #}#将渲染如下:value of i: 0 value of i: 1 value of i: 2
渲染模板
kendo.template()方法用于剑道 UI 框架中的模板化。它将编译后的模板作为 JavaScript 函数返回,该函数使用提供给它的数据呈现 HTML。用法很简单;将模板作为输入提供给kendo.template(),用输入数据调用编译后的函数生成 HTML。
内嵌模板
模板可以定义为 JavaScript 字符串(内嵌)或者为 HTML <script>块(外部)。简单的模板是内联定义的合适的候选对象,而更复杂的模板(带有 HTML 块和 JS 表达式)更适合在外部定义。
以下是内联模板的一个简单示例:
<div id="renderHere"> </div>
var kendoTemplate = kendo.template("This awesome HTML5 framework is called : #= frameworkName # !");
var localData = { frameworkName: "Kendo UI" };
$("#renderHere").html(kendoTemplate(localData));
前面代码的输出会显示这个牛逼的 HTML5 框架叫做:剑道 UI!
注
如果模板中需要一个#,需要用反斜杠(\)转义。在内联模板中,由于模板是 JavaScript 字符串,\必须在#之前使用。
在前面的例子中,如果我们想将输出显示为,那么#1 HTML5 框架就是剑道 UI!,那么我们需要将模板修改为:
var kendoTemplate = kendo.template("The \\#1 HTML5 framework is #= frameworkName # !");
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/LHfbg/
**### 外部模板
外部模板在<script>块内的 HTML 文件中定义,类型为text/x-kendo-template,如图所示:
<script id="kendoExternalTemplate" type="text/x-kendo-template" >
<!--Template content goes here-->
</script>
因为它们是在代码之外定义的,所以很容易编码和维护。外部模板应该有一个标识,因为它是用于选择模板内容的句柄。现在让我们看一个更复杂的外部模板示例。
在本例中,我们将使用包含移动操作系统名称和版本号的数据数组。如果版本过时,将显示一条消息,要求用户升级操作系统。
在模板中,我们使用转义字符在输出窗口中显示#,模板使用kendo.render(template, data)方法渲染。
注
kendo.render()方法用于渲染 JS 对象的数组。
<!-- div in which template will be rendered -->
<div id="renderHere"></div>
<!-- External Template definition -->
<script id="kendoExternalTemplate" type="text/x-kendo-template">
# switch (data.osName) {
case "iOS":
if (data.version < 6.1) {
# <div> Your iOS version \# #= data.version# needs to be updated </div>
#}
break;
case "Android":
if(data.version < 3 ){#
<div> Your Android version \# #=data.version# needs to be updated </div > #
}
break;
}#
</script>
<script>
var localData = [{
osName: "iOS",
version: 6.1
}, {
osName: "Android",
version: 2.3
}, {
osName: "iOS",
version: 5.1
}, {
osName: "Android",
version: 4.2
}
];
var kendoTemplate = kendo.template($("#kendoExternalTemplate").html());
$("#renderHere").html(kendo.render(kendoTemplate, localData));
</script>
该代码的输出为:
你的安卓 2.3 版本需要更新
你的 iOS 版本# 5.1 需要更新
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/WpBZa/
MVVM
选择剑道 UI 而不是其他 HTML5 框架的另一个重要原因是对 MVVM 的内置支持。大多数竞争框架需要开发人员使用第三方 JS 框架,如击倒 JS 来实现 MVVM 模式。自从 MVVM 作为框架的一部分被提供以来,开发人员就不必担心集成问题和支持。
MVVM 设计图案
Model-View-View Model(MVVM)是微软演化而来的架构设计模式,试图将数据模型与用户界面分离。所有三名成员都有自己的责任,这清楚地将他们区分开来:
- 模型:包含数据点或应用数据(通常由应用逻辑层返回)。
- 视图:数据呈现给用户的是用户界面层。
- 视图模型:也是被称为视图的模型,充当视图和模型之间的中介。它将视图的抽象层创建为具有数据、命令和抽象的模型,通常聚合多个模型。
由于视图模型是视图及其数据的反映,因此对视图所做的更改可以自动反映在视图模型上,反之亦然,这是通过数据绑定 实现的。因此,通过使用 MVVM,编写的从数据源更新用户界面和从用户界面更新数据源的 JavaScript 代码量大大减少,因为这两个过程是自动发生的。开发人员只需要定义视图模型和用户界面元素之间的绑定。
类型
Addy Osmani 的一篇优秀文章见此链接,文章详细讨论了 JavaScript 开发人员的 MVVM:
剑道 MVVM 入门
剑道 MVVM 入门的过程很简单。我们要做的是:
- 使用
kendo.observable()从 JavaScript 数据创建一个可观察的视图模型 - 使用
kendo.bind()将视图模型中的方法和属性绑定到用户界面中的 HTML 元素
现在,当数据更改视图模型时,更改会反映在用户界面中,当用户界面数据因用户交互或任意代码而更改时,视图模型也会随着更改而自动更新。让我们看看如何使用一个简单的例子来实现这一点,稍后我们将进入复杂的例子:
HTML
<div id="mainView" >
<input type="text" id="item" data-bind="value: item" />
<input type="text" id="quantity" data-bind="value: quantity" />
<br/>
<label data-bind="text: description"> </label>
</div>
JavaScript
<script>
var observableViewModel = kendo.observable({
item: "gold",
quantity: "10 grams",
description: function(){
return "You bought " + this.get("quantity") + " of " + this.get("item") ;
}
});
//bind the view model
kendo.bind($("#mainView"),observableViewModel);
</script>
我们创建了一个可观察的视图模型,它有两个属性item和quantity以及一个函数。然后,属性被绑定到两个文本框和一个名为description的相关方法 ,该方法实时使用属性的值来创建绑定到标签的购买描述。

当文本框中的值发生变化时,它会自动反映在描述中。

在description功能中,我们已经使用this.get()读取了quantity和item的值。get()方法用于获取observableViewModel对象中的属性值。同样,使用set方法设置值。
如果不使用get方法,就不会检测到对绑定的依赖,读取时也不会反映更新后的值。同样,当视图模型的单个属性被修改时,我们需要使用set方法,以便对绑定的依赖保持不变。
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/wY74g/
绑定
MVVM 绑定将 HTML 元素/小部件绑定到视图模型中的属性或方法。在前面的例子中,我们探索了text和value绑定:
<input type="text" id="item" data-bind="value: item"/>
<input type="text" id="quantity" data-bind="value: quantity"/>
<label data-bind="text: description"> </label>
剑道 MVVM 还支持其他几种绑定,以涵盖经常使用数据绑定的常见场景。以下是 MVVM 绑定的完整列表:
|有约束力的
|
描述
|
| --- | --- |
| 属性 | DOM 元素属性绑定到视图模型字段或方法。例如<a id="anchor" data-bind="attr: { href: url }"> Link </a> |
| 检查 | 它根据视图模型中的值检查/取消检查可检查的 DOM 元素和小部件。例如<input type="checkbox" data-bind="checked: IsSelected" /> |
| 点击 | 在这个绑定中,视图模型的一个方法被附加到被绑定元素的点击 DOM 事件。例如<a data-role="button"``data-bind="click:updateUserDetails"> Update </a> |
| 习俗 | 通过扩展kendo.data.Binder对象可以注册自定义绑定。如果您对更多信息感兴趣,可以在http://goo.gl/tFksg找到完整的文档 |
| 有缺陷的 | 绑定的 DOM 元素(仅适用于input、select和textarea)将被禁用,具体取决于视图模型的属性值或方法。例如<input type="text" data-bind="disabled: isDisabled" /> |
| 使能够 | 绑定的 DOM 元素(仅适用于input、select和textarea)将根据视图模型的属性值或方法启用。例如<input type="text" data-bind="disabled: isEnabled" /> |
| 事件 | 视图模型方法绑定到 DOM 事件。例如<input type="text" data-bind="value:randomText, events:{focus: onFocus}" /> |
| 超文本标记语言 | 目标 DOM 元素的 HTML 内容(innerHTML)绑定到视图模型的值。例如<div data-bind="html:htmlText"> </div> |
| 看不见的 | 绑定的 DOM 元素或小部件是隐藏还是显示取决于视图模型的值。例如<input type="text" data-bind="invisible: isInvisible" /> |
| 看得见的 | 根据视图模型的值,显示或隐藏绑定的 DOM 元素或小部件。例如<input type="text" data-bind="visible: isVisible" /> |
| 来源 | 目标元素的 HTML 内容是通过用视图模型的值渲染一个剑道模板来设置的,该模板被指定为元素的data-template属性。例如<ul data-role="listview" data-style="inset" id="usracc-bkng-hstry-list" data-template="usracc-booking-history-tmpl" data-bind="source: userBookingHistory"></ul> |
| 风格 | 这种绑定方式设置了目标 DOM 元素的style属性。例如<span data-bind="style:{color:movieColor, fontSize:movieFontSize }, text: movieName"></span> |
| 文本 | 目标 DOM 元素的文本内容绑定到视图模型的值。例如<label data-bind="text: description"> </label> |
| 价值 | 值绑定支持 HTML 元素输入,textarea和select。还支持所有具有值属性的小部件。例如<input type="text" id="item" data-bind="value: item" /> |
类型
剑道团队在以下链接中详细解释了所有绑定:
MVVM 在移动
剑道也在移动框架中为 MVVM 提供了出色的支持。使用model配置选项,移动视图中的所有小部件都可以绑定到视图模型。初始化移动视图时,使用model配置中提供的视图模型在其所有子元素上调用kendo.bind()。由于剑道为其网络和数据可视化小部件提供了出色的触摸支持,它们也可以添加到移动应用中。在这种情况下,移动视图将以相同的顺序绑定移动、网络和数据可视化小部件。
注
为了使声明性绑定起作用,正如本节前面所解释的,模型对象必须在应用的全局范围 中可用。
现在让我们看一下移动视图中的一个复杂示例,它使用模板和 MVVM。
下面的代码将在文本框中输入电影名称,并将其附加到电影列表中。通过点击 X 按钮,电影可以从列表中删除。
HTML
<div data-role="view" style="margin: 10 0 0 4" data-model="viewModel">
Movie:
<input type="text" data-bind="value: movie" />
<a data-bind="click: addMovie" data-role="button" id="btnAdd">Add</a>
<div style="margin-top: 10px">
Watched movies list:
<ul data-template="movie-list-template" data-bind="source: movieList">
</ul>
</div>
</div>
<!--Kendo template -->
<script id="movie-list-template" type="text/x-kendo-template">
<li>
Movie: <span data-bind="style:{color:movieColor,
fontSize:movieFontSize },
text: movieName"></span> |
Added: <span data-bind="text: addedDate,
style:{color:addedDateColor}"></span>
<a data-bind="click: removeMovie"
data-role="button" id="btnRemove" >X</a>
</li>
</script>
JavaScript
<script>
var app = new kendo.mobile.Application(document.body);
var viewModel = {
movie: '',
movieList: [],
addMovie: function (e) {
//when addMovie function is called, add the movie
// property which is bound to the movie text box to
//movieList array along with styles and added date
if (this.movie != '') {
this.get("movieList").push(
{ movieName: this.get("movie"),
movieColor: "green",
movieFontSize: "16px",
addedDate: new Date().toLocaleDateString(),
addedDateColor: "navy"
});
}
//clear the value in the text box.
this.set("movie", '');
},
removeMovie: function (e) {
alert('Remove: ' + e.data.movieName);
//Remove the movie name from the movieList array.
this.set("movieList",
jQuery.grep(this.movieList,
function (item, i) {
return (item.movieName != e.data.movieName);
}));
}
}
</script>
使用data-model属性将viewModel对象配置为移动视图的模型。添加按钮的click事件绑定到addMovie功能,在文本框中输入的文本被添加到movieList数组中。使用剑道 UI MVVM 源绑定 ,将movieList数组绑定到 ID 为:movie-list-template的模板。所以点击添加按钮时,电影列表会自动更新。removeMovie功能绑定到模板内的 X 按钮,将所选电影从列表中移除。
在模板内,应用于添加的电影名称和日期的样式使用剑道 MVVM 风格绑定 进行绑定。使用此绑定绑定的属性是movieList对象的movieColor、movieFontSize和addedDateColor。

类型
试试 js 提琴:http://jsfiddle.net/kendomobile/2PGkJ/
与电影票应用整合
现在我们已经介绍了剑道用户界面框架的一些重要元素,让我们把它们放在一起,通过创建用户帐户屏幕来看看这在现实应用中是如何工作的——电影票。我们还将把我们的网络应用编程接口服务与用户界面集成起来,并处理一些真实的数据。
用户账户屏幕
通过用户帐户屏幕,一个注册用户可以登录到应用。一旦用户登录,他/她就可以查看和更新他/她的详细信息。在该屏幕中,用户也可以看到他/她的订票历史。用户登录后,导航栏上显示一个注销按钮。
后端–设置网络应用编程接口服务
作为第一步,让我们用Account控制器、一个GET和一个POST方法来配置网络应用编程接口服务,并在存储库类中设置一些硬编码数据。
类型
本章中创建的服务方法可通过以下网址在线访问:
http://api.kendomobilebook.com/api/Account/
-
在
BusinessObjects\BLL文件夹中创建两个名为UserBO.cs和TicketsBO.cs的类。这些类别的对象将包含用户和票证详细信息:namespace MovieTickets.WebAPI.BLL { public class TicketBO { public int TicketId { get; set; } public string TheaterName { get; set; } public string MovieName { get; set; } public string Screen { get; set; } public int NoOfPersons { get; set; } public string ShowDate { get; set; } public string ShowTime { get; set; } } } namespace MovieTickets.WebAPI.BLL { public class UserBO { public string FirstName { get; set; } public string LastName { get; set; } public string UserName { get; set; } public string Password { get; set; } public string EmailId { get; set; } public string Address { get; set; } public bool SubscribedForNewsLetter { get; set; } public List<TicketBO> BookingHistory { get; set; } } } -
在
MovieRepository.cs类中,创建一个名为GetUserDetails的方法。该方法包含所有用户的硬编码数据及其订票历史,如下所示://This method will send user details back to the app public static UserBO GetUserDetails(string userName) { //2 users are created by default var usersList = new List<UserBO>{ new UserBO(){ Address = "123 North Field Pkwy, Buffalo Grove, Illinois", EmailId = "alison.massey@email.com", FirstName = "Alison", LastName = "Massey", UserName = "username1", SubscribedForNewsLetter = true, BookingHistory = new List<TicketBO>{ new TicketBO(){ TicketId = new Random().Next(10000), TheaterName = "AMC, South Barrington, IL", MovieName = "The Call", Screen = "12", NoOfPersons = 5, ShowDate = "15-Aug-2013", ShowTime = "4:30 PM" }, new TicketBO(){ TicketId = new Random().Next(10000), TheaterName = "Regal Cinemas, Lincolnshire, IL", MovieName = "Argo", Screen = "7", NoOfPersons = 2, ShowDate = "25-Aug-2013", ShowTime = "7:00 PM" } }, }, new UserBO(){ Address = "20627 Mauve Orchid Way,Dallas, TX ", EmailId = "patrick.dcoster@email.com", FirstName = "Patrick", LastName = "DCoster", UserName = "username", SubscribedForNewsLetter = false, BookingHistory = new List<TicketBO>{ new TicketBO(){ TicketId = new Random().Next(10000), TheaterName = "AMC NorthPark, North Central Expressway, Dallas, TX", MovieName = "Evil Dead", Screen = "19", NoOfPersons = 6, ShowDate = "13-July-2013", ShowTime = "4:30 PM" }, new TicketBO(){ TicketId = new Random().Next(10000), TheaterName = "AmStar 14, Technology Boulevard, Dallas, TX", MovieName = "The Host", Screen = "21", NoOfPersons = 1, ShowDate = "21-Sept-2013", ShowTime = "5:00 PM" } }, } }; return usersList.FirstOrDefault(x => x.UserName.Equals(userName)); } -
在我们在第 3 章、服务层用 ASP.NET Web API创建的
MovieTicketsBLL类中,创建一个名为GetLoggedInUserDetails的方法,该方法将从上一步创建的GetUserDetails方法中检索用户的详细信息和票证历史:public static UserBO GetLoggedInUserDetails(string userName) { return MovieRepository.GetUserDetails(userName); } -
现在在
AccountController(如果你还没有创建一个名为AccountController的网络应用编程接口控制器)中,创建两个名为Get和Post的方法,如下面的代码所示。Get方法会返回用户的详细信息,Post方法会更新用户的详细信息(同样,我们不会像使用硬编码数据那样以持久的方式更新详细信息;我们正在演示如何进行开机自检并检索控制器中的值。):[Authorize] public UserBO Get() { /* The user will be authenticated andThread.CurrentPrincipal is set in theValidateCredentials method of AuthMessagehandler class before the control reaches this action method */ MovieTicketsPrincipal currentPrincipal = Thread.CurrentPrincipal as MovieTicketsPrincipal ; return MovieTicketsBLL.GetLoggedInUserDetails(currentPrincipal.UserName); } [Authorize] public bool Post(UserBO updatedUserDetails) { //Code to update user details in the DB. //Since we are using hard coded values for the //demo, no save operation is done here. return true; }
前端–架构
现在我们的 Web API 已经为用户帐户屏幕准备好了两种操作方法,是时候关注我们移动视图的前端 UI 了。首先,让我们使用 jQuery/JavaScript 创建前端架构,以创建视图模型、数据访问方法和一些实用方法。我们将通过使用 JavaScript 闭包和实现封装,将这些模块作为单独的模块来对待。我们将在自己的文件中创建每个模块,并且只允许通过模块对象公开访问某些函数和变量。
显示模块模式
我们在 JavaScript 代码的示例应用中遵循的设计模式被称为揭示模块模式 ( RMP )。的概念很简单;我们将使用闭包在 JavaScript 中引入private和public封装。只有特别返回的方法和变量是公开的,我们可以在公共方法中提供对私有方法和变量的引用。让我们看一个用 RMP 编写的模块的例子:
var applicationModule = (function() {
//private variable. Existence limited within
//this closure
var privateVariable = 10;
var privateFunction = function() {
//Not accessible publicly
};
//accessibly publicly as //applicationModule.publiclyReturnPrivateVar
var publicFunction = function() {
return privateVariable;
};
return {
publiclyReturnPrivateVar: publicFunction
};
})();
//prints value of privateVariable
console.log(applicationModule.publiclyReturnPrivateVar());
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/gejfm/
我们创建了一个名为applicationModule的模块,它包含一个名为privateVariable的private变量、一个名为privateFunction的private函数和一个名为publicFunction 的public函数。方法publicFunction仅使用applicationModule.publiclyReturnPrivateVar对外开放。物体privateVariable和privateFunction在封闭物之外根本无法接近。最后一个括号导致函数被调用。
因此,利用 JavaScript 中的闭包,我们实现了其他传统面向对象语言中可用的封装。因此,我们可以像在 C#中一样,通过创建一个private对象并通过创建public方法来“获取”和“设置”private对象的值来实现Get和Set属性。
优势
通过实现揭示模块模式,我们可以使用 JavaScript 构建可扩展的和复杂的应用。这种模式的一些优点是:
- 将方法和属性隐藏在模块外部的能力
- 在调试器中显示调用堆栈更容易
- 它通过清晰区分
return语句中的public对象来增加可读性
注
如果你想探索更多,我们建议你阅读 Addy Osmani 的书学习 JavaScript 设计模式中关于模块模式和揭示模块模式的内容,可在以下链接中找到:
在大型项目中,设计您的 JavaScript 模块非常重要,因为团队变得越来越大,因为程序员在全局范围内滥用免费可用的 JavaScript 对象的机会越来越多。
名称间距
许多编程语言实现命名空间 以避免与全局命名空间中的其他对象或变量冲突。它还有助于将应用的代码分组到多个块中。名字间距在用 JavaScript 编写的应用中非常重要,因为在全球范围内发生冲突的风险很高。即使 JavaScript 没有内置名称空间,我们仍然可以使用 JavaScript 对象来提供名称空间支持。
在我们的电影票应用中,我们定义了一个全局对象作为我们的命名空间,如图所示:
var MovieTickets = MovieTickets || {};
在引用任何其他自定义 JS 文件之前,MovieTickets对象在<head> </head>部分中定义,因此这是我们的应用中第一个要初始化的对象。现在我们将创建其他模块作为这个对象的属性,这样就不会与我们可能使用的其他第三方 JS 库定义的名称有任何冲突。
例如,我们的configuration模块和common模块将作为:
MovieTickets.configuration
MovieTickets.common
配置
通常在每个软件项目中,都会有一个处理所有配置数据的模块。为此,在我们的项目中,让我们在scripts文件夹中创建一个名为configuration.js的文件。该模块存储所有可配置的数据,如网络应用编程接口服务的基础和相关的控制器网址:
MovieTickets.configuration = (function () {
var serviceUrl = "http://localhost/movietickets.webapi/api/";
return {
serviceUrl: serviceUrl,
accountUrl: serviceUrl + "Account/"
}
})();
现在,服务和帐户控制器 URL 可以通过以下方式在应用中使用:
MovieTickets.configuration.serviceUrl MovieTickets.configuration.accountUrl
如果您使用不同的网址来托管您的网络应用编程接口服务,请用您的网址替换private变量serviceUrl的值。
数据访问
现在我们需要创建一个可重复使用的数据访问模块,该模块将用于连接到网络应用编程接口并获取每个视图的数据。为此,在scripts文件夹中创建一个名为data-access.js的文件,并在其中添加以下代码:
MovieTickets.dataAccess = (function() {
//options input parameter will have all the data needed
//to perform the ajax call
function callService(options) {
$.ajax({
url: options.url,
type: options.requestType,
data: options.data,
dataType: options.dataType,
//Add HTTP headers if configured
beforeSend: function (xhr) {
if (typeof options.httpHeader !== 'undefined'
&& typeof options.headerValue !== 'undefined')
xhr.setRequestHeader(options.httpHeader,
options.headerValue);
},
//on successful ajax call back
success: function (resultData, status, xhr) {
var result = {
data: resultData,
success: true
};
options.callBack(result);
},
//Callback function in case of an error
error: function (xhr, status, errorThrown) {
switch (xhr.status) {
case '401':
alert('401 Unauthorized access detected.
Please check the credentials
you entered.' + errorThrown);
break;
case '500':
alert('500 Internal Server Error.
Please check the service code.'
+ errorThrown);
break;
default:
alert('Unexpected error: ' + errorThrown);
break;
}
var result = { success: false };
options.callBack(result);
}
});
}
return {
callService: callService
}
})();
该模块公开了一个名为MovieTickets.dataAccess.callService()的方法,该方法使用提供的网址调用服务,并执行在options对象中配置的操作:
options.url:要调用的网址options.requestType:请求类型,如 GET、POST、PUT、DELETE 等options.data:发送给服务的数据options.dataType:期望从服务器返回的数据类型,例如 JSON/XML 等options.httpHeader:需要随请求一起添加的 HTTP 头options.headerValue:添加的 HTTP 头的值options.callBack:收到响应后要调用的功能
使用选项对象中的这些配置,对服务进行 jQuery Ajax 调用,并调用callback函数。如果请求成功,从服务器返回的数据将与包含成功标志值true ( { success: true })的callback函数一起返回,如果遇到错误,将使用错误标志({ success: false })调用callback函数。
在 jQuery 的error功能中,我们根据常见的错误代码显示友好的错误信息,如 500 (内部服务器错误) 401 (未授权访问)等。
初始化
让我们从第二章、构建您的第一个移动应用中重新组织应用的初始化代码,以便为项目创建一个正式的结构。为了做到这一点,让我们在scripts文件夹中创建一个名为movie-tickets.js的文件,并将应用初始化代码以及一些其他代码移动到该文件中。名为MovieTickets.main的模块将作为应用的主要入口点:
MovieTickets.main = (function () {
var application;
function getApplication() {
return application;
}
function initializeApp() {
//initialize app
application = new kendo.mobile.Application(document.body,
{
transition: 'slide',
loading: "<h3>Loading...</h3>"
});
//Display loading image on every ajax call
$(document).ajaxStart(function () {
//application.showLoading calls the showLoading()
//method of the pane object inside the application.
//During the application's initial view's init
//method this pane object may not be initialized
//and so application.showLoading() may throw error.
//To prevent this we need to do a check for existence
//application.pane before calling
//application.showLoading();
if (application.pane) {
application.showLoading();
}
});
//Hide ajax loading image on after ajax call
$(document).ajaxStop(function () {
if (application.pane) {
application.hideLoading();
}
});
}
return {
initializeApp: initializeApp,
getKendoApplication: getApplication
}
})();
在本模块中,我们使用kendo.mobile.Application()方法初始化应用,并将初始视图设置为主页,将视图转换效果设置为slide,将 Ajax 加载文本设置为<h3>Loading...</h3>。一旦剑道用户界面移动应用被初始化,我们就使用 jQuery 的$(document).ajaxStart和$(document).ajaxStop事件来显示和隐藏加载消息,无论何时从我们的应用触发 Ajax 调用,从而避免了在 Ajax 调用中显式显示和隐藏加载消息的需要。
常用效用方法
现在,让我们在scripts文件夹中一个名为common.js的文件中创建模块,在这里可以编写多个模块调用的实用方法。我们将添加三种方法:
-
navigateToView(view):在这个方法中,我们使用剑道application对象的navigate()方法来编程导航到特定的视图。视图可以是本地视图、远程视图或外部视图。 -
showLogOffButton():这个方法,当被调用时,在导航栏上显示注销按钮(这个的 HTML 代码将在本章后面添加)。 -
hideLogOffButton():这个方法隐藏了导航栏上的注销按钮。MovieTickets.common = (function () { function navigateToView(view) { //Navigate to local/remote or external view MovieTickets.main.getKendoApplication().navigate(view); } function showLogOffButton() { //show log off button. $(".mt-main-layout-btn-logoff").show(); } function hideLogOffButton() { //hide log off button $(".mt-main-layout-btn-logoff").hide(); } return { navigateToView: navigateToView, showLogOffButton: showLogOffButton, hideLogOffButton: hideLogOffButton } })();
注
在navigateToView功能中,我们甚至可以使用#:back作为输入参数导航到上一个视图。
用户账户视图模型
由于现在已经有了项目集的基础,我们可以为用户帐户屏幕创建一个可观察的视图模型,它将使用data-model属性绑定到视图,如 MVVM 部分所述。让我们在scripts文件夹中创建一个名为user-account.js的文件,并添加以下代码:
MovieTickets.userAccount = (function () {
//ViewModel for User Account view
var viewModel = kendo.observable({
isUserLoggedIn: false,
firstName: "",
lastName: "",
userName: "username", //hardcoded
password: "password", //hardcoded
userAddress: "",
userEmailAddress: "",
subscribedForNewsLetter: false,
userBookingHistory: [],
userLogin: function () {
var loginOptions = {
url: MovieTickets.configuration.accountUrl,
requestType: "GET",
dataType: "JSON",
//for HTTP Basic authentication
httpHeader: "Authorization",
//btoa function will convert the text to
//base 64 encoding
headerValue: "Basic "
+ btoa(this.userName + ":" + this.password),
callBack: this.fnLoginCallBack
};
MovieTickets.dataAccess.callService(loginOptions);
},
//method for user login
fnLoginCallBack: function (result) {
if (result.success === true) {
viewModel.set("firstName", result.data.FirstName);
viewModel.set("lastName", result.data.LastName);
viewModel.set("userAddress", result.data.Address);
viewModel.set("userEmailAddress",
result.data.EmailId);
viewModel.set("userBookingHistory",
result.data.BookingHistory);
viewModel.set("isUserLoggedIn", true);
viewModel.set("subscribedForNewsLetter",
result.data.SubscribedForNewsLetter);
MovieTickets.common.showLogOffButton();
} else {
//any error handling code
}
},
//method to update user details
updateUserDetails: function () {
var updateOptions = {
url: MovieTickets.configuration.accountUrl,
requestType: "POST",
dataType: "JSON",
data: {
firstName: viewModel.get("firstName"),
lastName: viewModel.get("lastName"),
address: viewModel.get("userAddress"),
emailId: viewModel.get("userEmailAddress"),
subscribedForNewsLetter:
viewModel.get("subscribedForNewsLetter")
},
//for HTTP Basic authentication
httpHeader: "Authorization",
//btoa function will convert the text to
//base 64 encoding
headerValue: "Basic " + btoa(this.userName + ":"
+ this.password),
callBack: function () {
//if you are using PhoneGap to deploy
//as an app, you should use the
//notification api
alert('Details updated...');
}
};
MovieTickets.dataAccess.callService(updateOptions);
},
//method called when log off button is clicked
logOff: function () {
console.log('inside logOff');
viewModel.set("firstName", "");
viewModel.set("lastName", "");
viewModel.set("userAddress", "");
viewModel.set("userEmailAddress", "");
viewModel.set("userBookingHistory", "");
viewModel.set("isUserLoggedIn", false);
//hide log off button
MovieTickets.common.hideLogOffButton();
//navigate to User Account screen.
MovieTickets.common.navigateToView("UserAccount.html");
}
});
return {
viewModel: viewModel
}
})();
在前面的视图模型代码中,我们创建了与用户详细信息相关的属性,例如名字、姓氏、等。我们还为显示在用户帐户屏幕上的用户预订历史创建了属性。可通过mtUserAccount.viewModel访问的视图模型将使用data-model属性绑定到视图。我们还在视图模型中添加了以下功能,这些功能将从用户帐户屏幕中调用:
-
userLogin():此功能用于通过调用AccountController's方法的Get动作方法登录系统。由于Get方法是使用[Authorize]属性的安全方法,我们在 HTTP 头中发送用户的用户名和密码(当属性绑定到相应的字段时将可用)用于基本身份验证。要进行 Base64 编码,我们使用btoa()功能。 -
fnLoginCallBack():当收到服务对登录 Ajax 调用(callback)的响应时,调用此功能。如果登录调用成功,将使用服务中的值设置视图模型属性。isUserLoggedIn属性也设置为true,表示用户已登录该应用。当用户导航到“用户帐户”屏幕时,此属性将决定是向用户显示用户登录屏幕还是用户详细信息屏幕。然后调用MovieTickets.common.showLogOffButton()方法,在导航栏中显示注销按钮。 -
updateUserDetails(): The updated values of thefirstName,lastName,address,emailId, andsubscribedForNewsLetterproperties which are collected from the User Account screen are sent to the Web API for saving.为此,我们使用编码为 Base64 值的用户凭证调用帐户控制器的
POST方法,这些值作为标头发送。 -
Logoff():此方法用于从视图模型中清除用户详细信息并注销用户。它还会将用户重定向到用户帐户屏幕,因为用户可以从应用中的任何位置注销。为此,我们使用MovieTickets.Common模块的navigateToView功能。
注
因为我们使用的是基于无状态 HTTP 的服务,所以您不必通知该服务注销,因为该服务不会跟踪服务器端会话。
HTML 界面
你现在应该已经厌倦了;当你真的想获得真正的剑道 UI Mobile 代码时,编写 JavaScript 代码。让我们马上开始,编写一些 HTML 代码,并带来一些剑道小部件、模板和绑定。
添加用户账户标签按钮
现在让我们打开index.html文件,它是我们在第 2 章、构建您的第一个移动应用中创建的,并在布局页脚中的TabStrip小部件上再添加一个按钮,以导航到用户帐户屏幕。我们正在使用随源代码一起提供的user_profile.ico图标文件作为按钮的图标。修改后的TabStrip代码如下:
<div data-role="tabstrip">
<a href="#mt-home-main-view">
<img src="img/movies.ico"
height="40" width="40" />
<br />
Movies
</a>
<a href="Trailers.html">
<img src="img/trailers.ico" height="40" width="40" />
<br />
Trailers
</a>
<a href="UserAccount.html">
<img src="img/user_profile.ico" />
My Account
</a>
<a href="#mt-about-view" data-icon="about">
<br />
About
</a>
</div>
现在在index.html文件中,用新的初始化代码替换第二章、构建您的第一个移动应用中的初始化代码:
<script>
//Kendo Mobile is initialized in this method
MovieTickets.main.initializeApp();
</script>
现在主屏幕看起来像下面的截图:

用户账户视图
我们在上一节新增新TabStrip按钮的时候,也已经将我的账号按钮链接到UserAccount.html了,现在我们要把这个视图创建为远程视图。在根文件夹中创建UserAccount.html,并创建一个具有两个div值的视图;一个用于登录部分,如果用户没有登录,将显示该部分,另一个部分将在用户登录后显示用户详细信息。然后,我们将使用data-model="movieTickets.userAccount.viewModel"将视图绑定到用户帐户视图模型。然后使用剑道 MVVM 不可见绑定将登录部分的可见性绑定到视图模型的isUserLoggedIn属性(参见绑定部分)。同样,用户详细信息部分使用剑道 MVVM 可见绑定绑定到属性isUserLoggedIn,因为我们需要在用户登录时显示该部分。
<div data-role="view" data-layout="mt-main-layout"
data-title="Account"
data-model="MovieTickets.userAccount.viewModel"
id="mt-theaters-view">
<!-- This section is bound by Kendo MVVM Invisible binding -->
<div id="usracc-login-section"
data-bind="invisible: isUserLoggedIn">
</div>
<div id="usracc-user-details"
data-bind="visible: isUserLoggedIn">
</div>
</div>
登录部分
现在让我们添加一些字段并绑定到两个部分。对于登录部分,让我们定义一个剑道ListView小部件,并添加用于输入用户名和密码的字段以及登录按钮。ListView小部件在这里只是用来排列元素的,下一章会详细讨论。然后,我们将它们绑定到视图模型中相应的属性和函数:
<ul data-role="listview" data-style="inset">
<li>
<label>User Name: </label>
<input type="text" data-bind="value:userName"
id="user-acc-username" />
</li>
<li>
<label>Password: </label>
<input type="text" id="user-acc-password"
data-bind="value:password" />
</li>
</ul>
<div class="centerAlign">
<a data-role="button" id="user-acc-login-button"
data-bind="click:userLogin">Login </a>
</div>

用户详细信息部分
接下来,让我们为用户详细信息部分添加用户界面元素,其中包含显示用户详细信息的文本字段和显示用户预订历史的模板化ListView。
首先,我们将创建一个 Kendo 模板来显示用户的预订历史,该模板将被添加到视图外部的文件底部:
<script type="text/x-kendo-template"
id="usracc-booking-history-tmpl">
Movie: <span class="valueText"> #=MovieName #</span>
Tickets: <span class="valueText"> #=NoOfPersons # </span>
<br/>
Theater:<span class="valueText"> #=TheaterName # </span>
<br/>
Screen: <span class="valueText">#=Screen # </span>
Show Date: <span class="valueText"> #=ShowDate# </span>
<br/>
Time: <span class="valueText">#=ShowTime # </span>
</script>
该模板将显示预订的门票的详细信息,例如,电影名称、门票数量、影院地址、屏幕号码、放映日期和放映时间。
现在我们将使用以下代码更新 ID 为usracc-user-details的用户详细信息div:
<div id="usracc-user-details"
data-bind="visible: isUserLoggedIn ">
<div>
<ul data-role="listview">
<li>First Name
<input type="text" id="usracc-firstName"
data-bind="value: firstName" />
</li>
<li>Last Name
<input type="text" id="usracc-lastName"
data-bind="value: lastName" />
</li>
<li>Address
<input type="text" id="usracc-address"
data-bind="value: userAddress" />
</li>
<li>Email
<input type="text" id="usracc-emailAddress"
data-bind="value: userEmailAddress" />
</li>
<li>Newsletter
<span style="text-align: left">
<input type="checkbox" id="usracc-newsletter"
data-role="switch"
data-bind=
"checked: subscribedForNewsLetter" />
</span>
</li>
</ul>
</div>
<br />
<div class="centerAlign">
<a data-role="button" id="usracc-btn-update"
data-bind="click:updateUserDetails">Update </a>
</div>
<!--Booking History section-->
<div>
<h3>Booking History </h3>
<!--Render template using MVVM Source binding -->
<ul data-role="listview" data-style="inset"
id="usracc-bkng-hstry-list"
data-template="usracc-booking-history-tmpl"
data-bind="source: userBookingHistory">
</ul>
</div>
</div>
在前面的代码中,我们添加了显示用户详细信息的字段和一个更新按钮。我们使用了剑道开关小部件显示用户的简讯订阅状态,其checked状态绑定到subscribedForNewsLetter属性。这个小部件将在下一章详细讨论。当我们点击更新按钮时,它会调用视图模型中的updateUserDetails函数,该函数会将更新后的用户详细信息作为 HTTP POST 请求发送给服务。在用户详细信息部分之后,使用ListView小部件呈现预订历史模板,数据源作为视图模型中的userBookingHistory属性。
一旦用户使用用户名/密码或用户名 1/密码 1 等凭据登录到应用,用户详细信息将显示在视图上,如下图所示:

修复跨域访问问题
如果您在一个域中托管您的网络应用编程接口服务,而您的移动应用在另一个域中托管(或者如果您试图从手机上打开的移动网站访问您电脑上托管的网络应用编程接口),您可能会看到您对网络应用编程接口服务的 Ajax 调用被阻止。发生这种情况是因为在网络浏览器中实施的同源策略 限制了对发出调用的资源的域的调用。用 JSONP(带填充的 JSON)代替 JSON 是常见的解决方案,但被认为是黑客,推荐的解决方案是让你的服务 CORS ( 跨来源资源共享)启用。CORS 是一个 W3C 标准,它允许网页向不同的域发出 Ajax 请求。这个标准放松了浏览器强加的同源策略。
注
如果您想详细探索 CORS,这里有一篇来自 Mozilla 开发者网络的好文章,可通过以下链接获得:
我们可以通过向网络应用编程接口的web.config文件的<system.webServer> </system.webServer>部分添加以下配置,使我们的网络应用编程接口服务在全球范围内启用 CORS:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin"
value="*"/>
<add name="Access-Control-Allow-Headers"
value="accept, authorization, origin, content-type"/>
<add name="Access-Control-Allow-Methods"
value="GET, POST, PUT, DELETE, OPTIONS"/>
</customHeaders>
</httpProtocol>
前面的配置允许来自任何来源的带有头的请求接受方法类型 GET、POST、PUT、DELETE 和 OPTIONS 的授权、来源和内容类型。
在对您的web.config进行此更新后,您可以将您的网络应用编程接口服务托管在xyz.com的一个域中,而您的网络应用托管在另一个域中,例如,abc.com可以使用 Ajax 访问网络应用编程接口的服务方法!
注
在写这本书的时候,ASP.NET 网络应用编程接口提供了内置的 CORS 支持,可以通过夜间构建访问。姚黄·林在以下网址提供了如何使用新的 CORS 支持的详细说明:
总结
在本章中,我们详细介绍了一些重要的剑道用户界面框架元素,如数据源、模板和 MVVM。我们还将第三章、服务层创建的 Web API 服务与 ASP.NET Web API集成,与电影票 app 集成,开发了用户账号界面。我们在电影票应用中实现了 HTTP 基本身份验证,以访问一些安全的操作方法。使用电影票应用,我们还讨论了如何使用显示模块模式构建剑道用户界面移动应用。在下一章中,我们将深入剑道 UI Mobile,了解更多剑道 UI Mobile 小部件,并进行大量的实践练习!**
五、探索移动小部件
Kendo UI Mobile 允许程序员通过提供各种可主题化的小部件来快速开发移动应用。这些小部件是为基于触摸的移动设备量身定制的,并提供具有原生外观和感觉的平台特定渲染。在第 2 章构建你的第一个移动应用中,我们了解了剑道移动应用的基本结构和一些小部件。在本章中,我们将从基础开始探索剑道手机小部件,并通过示例进行详细介绍。
在本章中,我们将介绍:
- 剑道手机小部件基础
- 移动用户界面小部件
- 列表视图
- 纽扣
- 按钮组
- 转换
- 窗格
- 酥脆饼
剑道手机小部件基础
所有剑道移动小部件继承自基类kendo.mobile.ui.Widget,基类继承自所有剑道小部件(网络和移动)的基类kendo.ui.Widget。下图显示了移动小部件类的完整继承链:

kendo.Class作为大多数剑道 UI 对象的基类,而kendo.Observable对象包含事件的方法。kendo.data.ObservableObject是剑道 MVVM 的基石,传承自kendo.Observable。
基于移动小部件的方法
从到继承链,所有剑道手机小部件都继承了一套常用的方法。在构建高性能、复杂的移动应用时,需要彻底了解这些方法。
注
Kendo UI Mobile 仅支持基于 WebKit 的浏览器,因此使用 Chrome 或 Safari 浏览器在桌面上运行演示代码非常重要。
绑定
在kendo.Observable类中定义的 bind()方法将处理程序附加到事件。使用这种方法,我们可以将自定义方法附加到任何移动小部件上。bind()方法采用以下两个输入参数:
eventName:事件的名称handler: 事件引发时要触发的功能
以下示例显示了如何创建新的移动小部件并将自定义事件附加到该小部件:
//create a new mobile widget
var mobileWidget = new kendo.mobile.ui.Widget();
//attach a custom event
mobileWidget.bind("customEvent", function(e) {
// mobileWidget object can be accessed inside this function as //'e.sender' and 'this'.
console.log('customEvent fired');
});
事件数据在对象e中可用。引发事件的对象可以在函数中作为e.sender或使用this关键字访问。
触发
trigger()方法执行所有附加到触发事件的事件处理程序。该方法有两个输入参数:
eventName: 要触发的事件的名称eventData(可选):要传递给事件处理程序的特定于事件的数据
让我们通过修改为 bind 提供的代码示例来看看触发器是如何工作的:
//create a mobile widget
var mobileWidget = new kendo.mobile.ui.Widget();
//attach a custom event
mobileWidget.bind("customEvent", function(e) {
// mobileWidget object can be accessed
//inside this function as
//'e.sender' and 'this' .
console.log('customEvent fired');
//read event specific data if it exists
if(e.eventData !== undefined){
console.log('customEvent fired with data: '
+ e.eventData);
}
});
//trigger the event with some data mobileWidget.trigger("customEvent", { eventData:'Kendo UI is cool!' });
这里我们使用bind()方法触发附加的自定义事件,并发送一些数据。这些数据在事件内部读取并写入控制台。
运行这段代码时,我们可以在控制台中看到以下输出:
customEvent fired
customEvent fired with data: Kendo UI is cool!
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/9grnR/
解除绑定
unbind()方法将先前附加的事件处理程序从小部件中分离出来。它采用以下输入参数:
eventName:待拆事件的名称。如果未指定事件名称,则将分离所有事件的所有处理程序。handler: 要分离的处理函数。如果未指定函数,则附加到该事件的所有函数都将被分离。
以下代码将事件附加到小部件,并在事件被触发时将其分离:
//create a mobile widget
var mobileWidget = new kendo.mobile.ui.Widget();
//attach a custom event
mobileWidget.bind("customEvent", function(e) {
console.log('customEvent fired');
this.unbind("customEvent");
});
//trigger the event first time
mobileWidget.trigger("customEvent");
//trigger the event second time
mobileWidget.trigger("customEvent");
输出:
customEvent fired
从输出中可以看出,即使我们触发了两次事件,只有第一次调用了事件处理程序。
一
one()方法与bind()方法相同,只有一个例外;处理程序在第一次调用后被解除绑定。所以处理程序只会被触发一次。
为了查看这个方法的运行情况,让我们在现有的示例代码中添加一个 count 变量,并跟踪处理程序被调用的次数。为此,我们将使用one()绑定事件处理程序,然后触发事件两次,如以下代码所示:
//create a mobile widget
var mobileWidget = new kendo.mobile.ui.Widget();
var count = 0;
//attach a custom event
mobileWidget.one("customEvent", function(e) {
count++;
console.log('customEvent fired. count: ' + count);
});
//trigger the event first time
mobileWidget.trigger("customEvent");
//trigger the event second time
mobileWidget.trigger("customEvent");
输出:
customEvent fired. count: 1
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/TnF3e/
如果用bind()方法替换one()方法,可以看到处理程序将被调用两次。
类型
前面讨论的所有方法(bind()、trigger()、unbind()和one())不仅限于小部件,还可用于任何源自kendo.Observable的剑道对象。
摧毁
destroy()方法继承自kendo.ui.Widget基础对象。destroy()方法删除所有事件处理程序附件,并删除jquery.data()属性中的小部件对象,这样小部件就可以安全地从 DOM 中删除,而不会出现内存泄漏。如果有可用的子部件,也将调用子部件的destroy()方法。
让我们看看destroy()方法如何使用剑道移动按钮小部件和浏览器的开发者工具控制台工作。创建一个 HTML 文件,在文件中添加以下代码以及 Kendo UI Mobile 文件引用,并在浏览器中打开它:
<div data-role="view" >
<a class="button" data-role="button" id="btnHome" data-click="buttonClick">Home</a>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function buttonClick(e){
console.log('Inside button click event handler...');
$("#btnHome").data().kendoMobileButton.destroy();
}
</script>
在这个代码块中,我们创建了一个剑道按钮小部件,在点击事件中,我们调用了按钮的destroy()方法。

类型
网上试试:http://kendomobilebook.com/chapter5/destroy.html
现在打开浏览器的开发者工具控制台窗口,输入$("#btnHome").data()并按进入。

现在,如果您点击前面截图中显示的对象链接,可以看到所有属性的详细视图:

现在在控制台中反复点击剑道图标,输入$("#btnHome").data()并点击进入。现在我们可以看到kendomoliblebutton对象从对象列表中移除:

即使数据对象消失了,按钮仍然留在 DOM 中,没有任何与之相关的数据或事件。
类型
如果你也想把按钮从 DOM 中移除,你可以调用jQuery.remove()方法,如图所示:
$("#btnHome").remove();
视图
view()方法特定于移动小部件,它返回加载小部件的视图对象。
在前面的例子中,我们可以给视图分配一个 IDmainView,然后使用this.view().id在按钮的点击事件中检索它,如下面的代码片段所示:
<div data-role="view" id="mainView" >
<a class="button" data-role="button" id="btnHome" data-click="buttonClick">Home</a>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function buttonClick(e){
console.log("View id: " + this.view().id);
}
</script>
输出:
View id: #mainView
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/xh4y7/
view()方法有用的一个场景是小部件的事件。如果需要访问同一视图中的另一个元素/小部件,我们可以通过使用当前视图的 ID 缩小搜索范围来提高选择元素的性能。
假设在前面的例子中,我们在同一个视图中有一个 ID 为htmlElement的元素,我们可以使用$(this.view().id).find("#htmlElement")选择这个元素,如果 DOM 中加载了多个视图,那么这个元素的表现会比$("#htmlElement")更好,因为 jQuery 现在需要遍历的 HTML 元素更少了。
移动用户界面小部件
现在让我们进入正题,探索更多剑道 UI Mobile 小部件。我们已经在第 2 章、构建您的第一个移动应用中使用了一些小部件,在本章中我们将详细了解更多小部件。
这些是剑道用户界面移动框架提供的小部件:
- 行动表
- 纽扣
- 按钮组
- 列表视图
- modalview(模式检视)
- 纳巴尔
- 酥脆饼
- 背景常速滚动的电脑游戏
- 卷动检视
- 转换
- 选项卡条带
- 触控
- 抽屉(2013 年在 Q2 推出)
现在让我们浏览一下所有对您来说是新的小部件,研究它们是如何工作的,在这一章的最后,我们将把这些小部件集成到我们的电影票应用中。
注
本章的目的是让您开始使用剑道移动小部件,并解释一些重要的属性和方法、它们的用法以及一些重要的提示。我们没有提供所有小部件的所有属性和方法的完整文档,因为在每个季度发布的版本中,属性和方法都被添加/弃用,因此 Kendo 在http://docs.kendoui.com/api/mobile提供的 API 参考文档是您获得框架完整参考的最好朋友。
初始化并显示视图小部件的事件
剑道 UI 移动视图小部件暴露了两个重要事件:init和show。这些事件的事件处理程序可以使用数据属性进行连接,如图所示:
<div data-role="view" data-init="onInitialize"
data-show="onShow">
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function onInitialize(e) {
alert('view initialized');
}
function onShow(e) {
alert('view shown');
}
</script>
类型
在 http://jsfiddle.net/kendomobile/vWC39/试试吧
在视图及其子部件初始化之后,init事件首先被触发,并且只触发一次。只要视图变得可见,就会触发show事件。init事件被设计用于在视图的生命周期中只应执行一次的操作,例如,使用 JavaScript 代码以编程方式创建一个小部件。这不能在show事件中完成,因为每当视图变得可见并导致意外结果时,代码都会尝试创建小部件。
show事件应该用于需要在视图显示时发生的动作的场景,例如,每当视图变得可见时,用来自服务器的最新数据更新小部件。
“视图”小部件还公开了三个事件:
beforeShow:该事件在视图可见之前触发afterShow:该事件在视图可见后触发hide:当视图隐藏时,触发此事件
列表视图小部件
在移动设备中,列表是使用最广泛的用户界面组件。列表以不同的方式进行样式化和修改,以便显示具有复杂结构的简单数据列表。剑道用户界面提供了一个非常强大的列表小部件,称为列表视图,可用于显示平面、分组或自定义模板列表。当data-role="listview"属性被添加到列表元素时,剑道会自动将任何 HTML 列表转换为移动优化列表视图小部件:
<ul data-role="listview">
<li>Olympus Has Fallen</li>
<li>Jurassic Park 3D</li>
<li>G.I. Joe: Retaliation</li>
</ul>
与任何其他 Kendo 小部件一样,ListView 也可以通过编程方式初始化,使用 jQuery 插件语法,如以下代码片段所示:
<div data-role="view" data-init="initialize">
<ul id="myList"></ul>
</div>
<script>
function initialize(){
$("#myList").kendoMobileListView({
dataSource: ["Olympus Has Fallen",
"Jurassic Park 3D",
"G.I. Joe: Retaliation"]
});
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/72mKp/

嵌入样式
我们在前面部分看到的代码片段将显示一个占据整个屏幕宽度的列表。如果您对这种体验不满意,并且需要您的列表像 iOS 设备一样有边距和圆角,您可以将属性data-style="inset"添加到<ul>元素中。此设置不适用于安卓或黑莓设备:
<ul data-role="listview" data-style="inset">
<li>Olympus Has Fallen</li>
<li>Jurassic Park 3D</li>
<li>G.I. Joe: Retaliation</li>
</ul>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/g9GRs/

上图显示了带有插图样式的列表。
链接
如果你的列表有锚点标签,Kendo ListView 会自动给列表添加右箭头指示,这样用户就可以直观的看到列表中的项目是可以点击的,可以进行详细查看或导航:
<ul data-role="listview" data-style="inset">
<li><a>Olympus Has Fallen</a></li>
<li><a>Jurassic Park 3D</a></li>
<li><a>G.I. Joe: Retaliation</a></li>
</ul>

详细按钮和图标
详细按钮小部件是尺寸较小的按钮图标,通常用于在有限的空间内需要多个按钮时节省空间。默认支持的四种不同风格分别是:contactadd、detaildisclose、rowinsert、rowdelete:
<div data-role="view">
<ul data-role="listview" data-style="inset">
<li><a>Olympus Has Fallen </a></li>
<li><a>Jurassic Park 3D</a></li>
<li><a>G.I. Joe: Retaliation</a></li>
</ul>
<ul data-role="listview" data-style="inset">
<li>Add Contact<a data-role="detailbutton" data-style="contactadd"></a></li>
<li>More Details<a data-role="detailbutton" data-style="detaildisclose"></a></li>
<li>Insert Movie<a data-role="detailbutton" data-style="rowinsert"></a></li>
<li>Delete Movie<a data-role="detailbutton" data-style="rowdelete"></a></li>
</ul>
</div>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/vhXRZ/

也可以使用<img>元素或使用剑道提供的图标的data-icon属性将项目图标添加到详细按钮中。剑道对使用data-icon属性生成的图标使用字体图标。这些内置字体图标只能在锚点标签中使用。
剑道有以下内置字体图标:
| 关于;在…各处 ;大约 | 行为 | 增加 | 电池 | 书签 | | 照相机 | 手推车 | 构成 | 联系人 | 细节 | | 下载 | 快进 | 收藏夹 | 作为特色的 | 顶级的 | | 地球 | 历史 | 家 | 信息 | 更多 | | 采样 | 最受关注 | 组织 | 中止 | 玩 | | 最近的 | 恢复精神 | 回答 | 重绕 | 搜索 | | 设置 | 分享 | 停止 | 废物 | |类型
也可以创建自定义图标用作按钮图标。由于细节不在本书的讨论范围内,我们建议您浏览以下网站上的剑道文档:http://goo.gl/v3yLj
分组和模板
剑道列表视图可以配置为将项目组织成组,每组有标题。这是通过将类型属性设置为group来完成的:
<div data-role="view">
<ul data-role="listview" data-type="group">
<li>
Horror
<ul>
<li>Evil Dead</li>
<li>Scream</li>
<li>Dark Skies </li>
</ul>
</li>
<li>
Sci-Fi
<ul>
<li>Prometheus</li>
</ul>
</li>
</ul>
</div>
通过将fixedHeaders属性设置为true,可以固定标题。滚动时,固定标题保持其位置,直到其下的所有项目都向上滚动。
剑道模板可用于自定义列表项。在用列表视图实现模板时,请记住列表视图会自动将模板内容包装在一个<li>标签中。在模板定义中添加一个<li>标签会打乱你的列表视图,所以只有需要在<li> </li>里面的项目才应该在模板中。让我们看看创建一个模板化的列表视图有多容易,该列表视图具有固定的标题和从数据源绑定的数据:
<body>
<div data-role="view" id="mainView" data-init="loadListView">
<ul id="listView"></ul>
</div>
<script type="text/x-kendo-template" id="listviewTemplate">
<a > <strong > #:movieName# </strong>
<i> #:dateTime#</i></a>
</script>
<script>
var app = new kendo.mobile.Application(document.body);
//create datasource
var movieDataSource = new kendo.data.DataSource({
data:
[
{
movieName: "Evil Dead",
dateTime: "10/7/2013 7:30PM", genre: "Horror"
},
{
movieName: "Scream",
dateTime: "10/7/2013 8:30PM", genre: "Horror"
},
{
movieName: "Hangover III",
dateTime: "10/7/2013 9:00PM", genre: "Comedy"
},
{
movieName: "Identity Thief ",
dateTime: "10/7/2013 1:15PM", genre: "Comedy"
},
{
movieName: "Seven Psychopaths",
dateTime: "10/7/2013 4:00PM", genre: "Comedy"
},
{
movieName: "Elysium",
dateTime: "10/7/2013 7:00PM", genre: "Sci-Fi"
},
{
movieName: "Prometheus",
dateTime: "10/7/2013 12:45PM", genre: "Sci-Fi"
}],
group: "genre"
});
//instantiate the list view
function loadListView() {
$("#listView").kendoMobileListView({
dataSource: movieDataSource,
template: $("#listviewTemplate").html(),
headerTemplate: "#:value#",
fixedHeaders: true,
style: 'inset'
});
}
</script>
</body>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/n96s2/
要查看 that 中的滚动操作,您可能必须减小浏览器的窗口大小,以便结果窗口中的列表元素可以滚动。
在这段代码片段中,我们使用本地数据创建了一个数据源对象movieDataSource,然后将其分配给列表视图的dataSource属性。然后,我们将已定义的带有标识listviewTemplate的模板分配给渲染项目列表。headerTemplate设置为#:value#,这意味着数据源的group属性中定义的属性值将显示为标题。我们也可以在标题模板中添加 HTML 元素,并像对待任何其他模板一样对待它。
标题设置为fixed,样式设置为inset。以下是滚动时列表的外观:

如您所见,当滚动时,静态标题将占用当前节的标题名称。
按钮部件
我们在第 2 章、构建您的第一个移动应用中了解到剑道移动按钮小部件。在本章中,我们将了解更多关于这个小部件以及如何定制它。Button 是 Kendo UI Mobile 小部件堆栈中最简单的小部件之一,有几个方法,只有与之关联的点击事件。它用于导航到视图(本地或远程)或在触发 click 事件时调用 JavaScript 函数。

通过设置角色数据属性,可以使用锚点标签或 HTML5 <button>标签以声明方式初始化按钮:
<div data-role="view">
<br/>
<button data-role="button" data-click="buttonClick" >
My Kendo Button
</button>
<a data-role="button" data-click="buttonClick" >
My Kendo Anchor Button
</a>
</div>
按钮使用 jQuery 插件语法初始化,如图所示:
<a id="kendoButton" >
Another Button
</a>
var button = $("#kendoButton").kendoMobileButton({
click: buttonClick
});
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/vzsbF/
图标
剑道提供的任何图标或您的自定义图标都可以设置为图标属性值:
var button = $("#kendoButton").kendoMobileButton({
icon:"globe"
});
默认情况下,首先显示图标,然后显示按钮上的文本。如果需要在文本后显示按钮,可以添加以下 CSS:
#kendoButton .km-icon{
float: right;
padding-left: 10px;
}

按钮组小部件
有时为了在视图内的部分之间导航,我们可能需要组合在一起的按钮。在这种情况下,按钮组小部件通过将一个 HTML 列表转换成一组按钮来提供帮助。也可以像按钮小部件一样为每个单独的按钮设置图标。

初始化
按钮组可以通过将data-role="buttongroup"设置为<ul> </ul>来初始化,如下代码片段所示:
<ul data-role="buttongroup" >
<li> Button 1 </li>
<li> Button 2 </li>
<li> Button 3 </li>
</ul>
按钮组也可以使用 jQuery 插件语法进行初始化,如下所示:
<div data-role="view" data-init="initialize">
<ul id="listButtons" >
<li> button 1 </li>
<li> button 2 </li>
<li> button 3 </li>
</ul>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function initialize(){
var buttongroup = $("#listButtons").kendoMobileButtonGroup();
}
</script>
造型
按钮组可以通过以下两种方式进行设置:
-
Adding your style definitions in the
<li>element for each button:<ul id="listButtons" data-role="buttongroup" > <li style="background-color:green; color:white; font-style:italic"> button 1 </li> <li> button 2 </li> <li style="background-color:orange; color:white;"> button 3 </li> </ul>![Styling]()
-
Overriding Kendo-generated CSS styles for iOS:
.km-root .km-ios .km-buttongroup .km-button { background-color: red; } .km-root .km-ios .km-buttongroup .km-button .km-text { color: white; }类型
试试 js 提琴:http://jsfiddle.net/kendomobile/XUke5/
单独设置
<li>元素的样式适合单独设置按钮的样式,当所有按钮的样式都不同于默认的剑道样式时,覆盖剑道样式效果最佳。
按钮组正在运行
现在,让我们编写一些代码,通过探索 ButtonGroup 小部件的属性、方法、和事件来看看它是如何工作的。我们的代码将创建一个具有四个按钮的按钮组,一旦选择了一个按钮,我们将向控制台写入按钮的文本和索引,并将按钮的文本更改为I am clicked。按钮将被配置为只有当我们释放按钮时才会选择按钮,这将确保在滚动时,触摸按钮组不会意外选择按钮。我们还将配置小部件,使按钮默认为green颜色,一旦选择了按钮,它会将其颜色更改为maroon:
<body>
<div data-role="view" data-init="initialize">
<div>
<ul id="btnGroup">
<li>button 1 </li>
<li>button 2 </li>
<li>button 3 </li>
</ul>
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function initialize() {
$("#btnGroup").kendoMobileButtonGroup({
index: 1,
selectOn: "up",
select: onButtonSelect
});
}
function onButtonSelect(e) {
console.log('selected button text was : '+ this.current().text());
console.log(" index:" + this.current().index());
//Change the text on the selected button
this.current().text('I am clicked');
}
</script>
</body>
<style>
#btnGroup .km-button
{
background-color: green;
color: white;
}
#btnGroup .km-state-active
{
background-color: maroon;
}
</style>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/xJ8JB/
在初始化期间,我们使用了按钮组小部件的几个属性:
index:该属性从组中选择具有指定索引的按钮。selectOn:利用这个属性,我们可以指定按钮是一按下就会被选中,还是用户松开按下的按钮就会被选中。允许值为up和down(默认)。将该属性的值设置为up将选择touchend、mouseup和MSPointerUp平台特定事件上的按钮选择,而down将选择touchstart、mousedown或MSPointerDown事件。
然后我们将选择事件连接到onButtonSelect功能。在该功能中,我们使用this.current().text()阅读按钮上的文本。current()方法将返回当前选中按钮的 jQuery 对象。同样,我们使用this.current().index()找到了当前所选按钮的索引。一旦使用 jQuery 的text()方法读取了当前按钮的数据,按钮的文本就会改变。
为了设置按钮的初始颜色,我们更新了.km-button CSS 类提供的样式。.km-state-active类为当前选择的按钮设置样式。通过在这个类中将背景颜色设置为maroon,我们改变了所选按钮的背景颜色。
我们也可以使用 ButtonGroup 的select(index)方法,以提供的索引作为输入选择一个按钮。
开关小部件
开关是移动设备上常用的 UI 元素,用于二进制开/关或真/假数据输入。可以点击或拖动开关小部件来切换其值。
剑道移动开关小部件是通过转换一个 HTML 复选框创建的。选中属性将获取或设置小部件的选中/未选中状态。选中状态的标签使用onLabel属性设置,而offLabel属性设置未选中状态的标签。
check()方法获取和设置小部件的选中状态,toggle()方法切换选中状态。当小部件的选中状态发生更改时,将触发更改事件。
初始化
现在让我们看看初始化 Switch 小部件的不同方式:
通过设置data-role="switch"进行数据属性初始化:
<input type="checkbox" id="chkSwitch1"data-role="switch" checked="checked"data-on-label="Pass"data-off-label="Fail" />
jQuery 插件语法:
<div data-role="view" data-init="init">
<input type="checkbox" id="chkSwitch2" />
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function init(){
$('#chkSwitch2').kendoMobileSwitch({
checked:false,
onLabel:'Yes',
offLabel:'No'
});
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/vk5XW/

窗格构件
窗格小部件用于在剑道移动应用的主视图中对多个视图进行分组。它通过允许导航到远程/本地视图、过渡效果、布局、设置默认视图、特定加载文本等,就像应用中的应用。窗格由窗口小部件(如 PopOver 和 SplitView)使用,以容纳其中的多个视图。
窗格小部件通过将角色数据属性设置为pane来初始化:
<div data-role="pane">
<div data-role="view" id="view1">
I am the 1st view
</div>
<div data-role="view" id="bar">
I am the 2nd view
</div>
</div>
方法
hideLoading 和showLoading方法隐藏和显示由窗格小部件的加载配置属性设置的加载动画。view()方法给出了对窗格上加载的当前视图的引用。
就像剑道应用对象一样,窗格小部件也有一个navigate(url, transition)方法,可以导航到窗格内外的视图。使用过渡输入配置可以提供不同的过渡效果。
默认情况下,窗格导航到当前窗格内的视图。要导航到其他窗格内的视图,目标数据属性在导航元素中设置为外部窗格的标识:
<a data-role="button" href="#outsideView"data-target="external-pane">External Pane</a>
要导航到应用内的其他视图(不在窗格内),目标属性的值设置为_top:
<a data-role="button" href="#mobileView"data-target="_top">Mobile View</a>
事件
窗格小部件有两个事件:
navigate(e):当导航到一个视图时,触发此事件。事件数据包含一个url属性(e.url,它有导航视图的网址。viewShow(e):当窗格小部件中显示视图时,触发此事件。事件数据包含一个view属性,该属性引用了加载的视图。
PopOver 小部件
PopOver 小部件用于平板设备上的在应用窗口上方浮动的视觉层上显示内容。该小部件通常用于以下场景:
- 显示关于元素的一些信息
- 显示用于导航到其他视图的菜单
- 显示包含要执行的操作的上下文菜单
- 要显示过滤器列表
PopOver 小部件可以包含多个视图,并且它们可以在彼此之间导航。视图会自动添加到窗格小部件中。可以使用窗格配置选项来设置窗格小部件的属性。
初始化
通过设置data-role="popover",小部件可以以声明方式进行初始化。可以通过data-popup属性设置其他弹出选项。
PopOver 小部件可以通过点击导航小部件来打开,方法是添加针对 PopOver ID 的data-rel="popover"属性和href属性,或者通过调用其open()方法以编程方式打开:
<a data-role="button" href="#popOverWidget"data-rel="popover">Filter</a>
<div id="popOverWidget" data-role="popover"data-popup="{'height':150}">
<div data-role="view">
<ul data-role="listview">
<li>
<a href="#">Comedy (9)</a>
</li>
<li>
<a href="#">Action (10)</a>
</li>
<li>
<a href="#">Romantic (5)</a>
</li>
<li>
<a href="#">War (7)</a>
</li>
</ul>
</div>
</div>
PopOver 小部件可以使用 jQuery 插件语法进行初始化,如以下代码片段所示:
<div data-role="view" data-init="init">
<a data-role="button" href="#popOverWidget"data-rel="popover">Select Genre</a>
<div id="popOverWidget" >
<div data-role="view">
<ul data-role="listview" >
<li>
<a href="#">Comedy (9)</a>
</li>
<li>
<a href="#">Action (10)</a>
</li>
<li>
<a href="#">Romantic (5)</a>
</li>
</ul>
</div>
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function init(){
$('#popOverWidget').kendoMobilePopOver({
popup: { height: '130px' }
});
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/cDBur/

多视图弹出窗口
我们会遇到许多场景,在 PopOver 小部件中,我们需要显示多个视图,并根据某些条件相互导航。一个常见的例子是多视图菜单,其中整个视图被另一个级别的菜单替换。
让我们看看如何使用 PopOver 小部件从类型列表中选择一部电影,然后从电影列表中选择一部电影,如下面的代码片段所示:
<head>
<style>
.no-backbutton .km-back {
visibility: hidden;
}
</style>
</head>
<body>
<div data-role="view" >
<a data-role="button" href="#popOverWidget" data-rel="popover">
Select Movie
</a>
<div id="popOverWidget" data-role="popover" data-pane="{'transition':'zoom','layout':'popoverLayout'}"data-popup="{'height':170}">
<!-- Layout -->
<div data-role="layout" data-id="popoverLayout">
<div data-role="header">
<a data-role="backbutton" > Back </a>
</div>
<div data-role="footer"></div>
</div>
<!-- main menu view-->
<div data-role="view" id="view-main"class="no-backbutton" >
<ul data-role="listview" >
<li><a href="#view-comedy">Comedy (3)</a></li>
<li><a href="#view-action">Action (2)</a></li>
</ul>
</div>
<!-- Comedy Menu View-->
<div data-role="view" id="view-comedy">
<ul data-role="listview" >
<li>
<a href="#view-final?movie=Hangover III">
Hangover III
</a>
</li>
<li>
<a href="#view-final?movie=Scary Movie">
Scary Movie
</a>
</li>
<li>
<a href="#view-final?movie=Ted">Ted </a>
</li>
</ul>
</div>
<!-- Action Menu View-->
<div data-role="view" id="view-action">
<ul data-role="listview" >
<li>
<a href="#view-final?movie=Iron Man 3">
Iron Man 3
</a>
</li>
<li>
<a href="#view-final?movie=After Earth">
After Earth
</a>
</li>
</ul>
</div>
<!-- Final View-->
<div data-role="view" id="view-final" data-show="finalViewShow">
<div>
<h3>You selected: </h3>
<h2>
<!-- Selected movie name will be displayed in this span -->
<span id="spanMovieName"> </span>
</h2>
</div>
</div>
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
//Function to be called when the view is shown every time
function finalViewShow(e) {
//show the movie name in the view
$('#spanMovieName').text(e.view.params.movie);
}
</script>
</body>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/ffjVh/
我们在 PopOver 小部件中添加了三个视图、两个菜单和一个最终视图,其中显示了选定的电影。菜单被分配了一个公共布局(popoverLayout),其中包含一个带有剑道后退按钮部件的标题元素。使用下面的 CSS 类在第一个视图中隐藏后退按钮,就像我们在第 2 章、构建您的第一个移动应用中所做的那样:
.no-backbutton .km-back { visibility: hidden; }
对于所有其他视图,后退按钮是可见的,通过使用它,我们可以导航到上一个视图。
在 ID 为view-main的视图中,我们正在显示电影的流派,点击链接将带您进入相应的电影菜单视图,即view-comedy或view-action。在此菜单中,我们将电影显示为链接,电影名称作为查询字符串传递给 ID 为view-final的视图。一旦我们点击电影名称并导航到最终视图,功能finalViewShow被调用。该函数从查询字符串中读取电影名称,并将其显示在视图上。
类型
我们在show事件中调用finalViewShow函数,而不是在init事件中,因为我们需要在每次显示视图时调用该函数。当视图初始化时,init功能将只被调用一次。

我们创建的 PopOver 小部件的第二个和第三个视图如下所示:

注
Kendo 提供了一个拖放主题构建器,使用它可以对小部件进行主题化,以匹配应用的外观和感觉。可以使用以下网址访问移动主题构建器:http://demos.kendoui.com/mobilethemebuilder/index.html
总结
在本章中,我们探讨了剑道用户界面移动小部件的基础知识,并了解了常用的方法。然后,我们深入研究了诸如列表视图、按钮、按钮组、开关、窗格和弹出窗口之类的小部件,并看到了如何使用 jshut 上可用的示例来实例化它们并在多个场景中使用它们。还讨论了一些关于调整小部件的未记录提示。在下一章中,我们将探索更多的剑道用户界面移动小部件。
六、动作表单、模式视图和更多小部件
在前一章中,我们学习了剑道 UI Mobile 小部件的基础知识,并探索了相当多的小部件。在这一章中,我们将继续这一势头,并使用 js 提琴实例探索更多的小部件。到本章结束时,您将成为使用所有剑道用户界面移动小部件的专家,我们将准备好将小部件集成到我们的电影票应用中。
在本章中,我们将介绍:
- 行动表
- modalview(模式检视)
- 拆分视图
- 背景常速滚动的电脑游戏
- 卷动检视
- 触控
- 抽屉
动作表单小部件
动作表用于移动设备(通常在移动电话上)上,向用户显示一个动作的多个选项。它们包含多个按钮,每个按钮都有一个文本。显示了一个典型的操作表,如下图所示:

剑道 UI Mobile 的 ActionSheet 小部件通过将一个<ul>转换为一个动作表,并在点击表上的项目时调用一个预配置的函数来提供该功能。如果我们将data-rel="actionsheet"和href属性设置为以哈希(#)为前缀的动作表单的元素标识,则可以在任何其他小部件中点击打开动作表单。
初始化
ActionSheet 可以声明式初始化,如下代码所示:
<div data-role="view">
<a data-role="button" data-rel="actionsheet"
id="tnShare " href="#confirmationSheet">
Share
</a>
<ul data-role="actionsheet"
id="confirmationSheet">
<li>
<a data-action="shareOnTwitter">
Twitter
</a>
</li>
<li>
<a data-action="shareOnFB">
Facebook
</a>
</li>
<li>
<a data-action="justEmail">
Email
</a>
</li>
</ul>
</div>
初始化,使用 jQuery 插件语法可以完成,如下面的代码片段所示:
<div data-role="view">
<a data-role="button" id="btnShare"
data-rel="actionsheet"
href="#confirmationSheet">Share</a>
<ul id="confirmationSheet">
<li>
<a data-action="shareOnTwitter">Twitter
</a>
</li>
<li>
<a data-action="shareOnFB">Facebook
</a>
</li>
<li>
<a data-action="justEmail">Email
</a>
</li>
</ul>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function initialize() {
$("#confirmationSheet").kendoMobileActionSheet();
}
function shareOnTwitter(e){
console.log('twitter...');
console.log(e.target);
}
function shareOnFB(e){
console.log('FB...');
console.log(e.target);
}
function justEmail(e){
console.log('Email...');
console.log(e.target);
}
</script>
类型
试试 js 提琴:
http://jsfiddle . net/kenomoble/zn4ew/
http://jsfiddle . net/kenomoble/5h qkr/
小部件也可以通过调用其open方法以编程方式打开。动作表单小部件中按钮的click事件可以使用data-action属性附加到一个函数。一个取消动作按钮自动添加到表单底部。
前面的代码在不同的平台上呈现不同。在 iPhone 中,小部件从底部作为模态对话框向上滑动,而在 iPad 中,它呈现在弹出式小部件中。以下截图分别来自 iPhone 和 iPad:

类型
自动生成的取消按钮的文本可以通过设置小部件的取消选项来更改,如下脚本所示:
<ul data-role="actionsheet" data-cancel="Custom Text"
id="confirmationSheet">
<li>.... </li>
</ul>
行动
连接到点击action按钮的功能接收对象作为输入。
该对象有两个参数:
target:这是 HTML 元素的 jQuery 对象,打开了 ActionSheet 小部件。在我们的例子中,它是按钮小部件$('btnShare')。context:这是使用小部件的data-actionsheet-context属性设置的值。此参数是可选的,可用于识别视图中定义了多个操作表时打开了哪个小部件。
打开和关闭
可以通过调用open方法以编程方式打开 ActionSheet 小部件。
open()方法采用两个输入参数:
target:这是动作函数中可用的 HTML 元素的 jQuery 对象。这个对象对于手机来说是可选的,但是对于平板电脑来说是必需的,因为小部件的位置取决于目标元素。context:这是一个可选值,可以作为动作函数输入参数中的上下文属性。
可以使用close()方法关闭小部件。close()方法没有输入参数:
$("#confirmationSheet").data("kendoMobileActionSheet").close();
在平板电脑上,ActionSheet 小部件以弹出小部件的形式打开。可以使用 ActionSheet 小部件的弹出属性来配置小部件的打开方向、高度和重量属性。
模式视图小部件
剑道手机 ModalView 小部件打开一个视图作为子窗口,这需要用户在返回主应用视图之前与之交互。它通常用于通过要求用户输入一些细节或显示非常重要的信息来吸引用户的全部注意力。典型的场景是定制的错误/确认消息、密码输入框等。
ModalView 小部件可以在视图内部或外部定义,并且可以有自己的布局。建议在移动应用中将 ModalView 的使用减少到最低限度,因为用户将被工作流打断,并在关闭琐碎的模式窗口时产生令人恼火的体验。
小部件的高度和宽度可以使用data-height和data-width属性来定义。要以编程方式打开小部件,我们可以在使用 jQuery data方法获取小部件的引用后使用open()方法。事件处理函数可以通过使用data-open属性连接到 ModalView 的open事件。
初始化顿
ModalView 小部件可以使用数据属性进行声明式初始化,如以下代码所示:
<div data-role="view">
<a href="#myModalView" data-rel="modalview"
data-role="button">
Open Modal View
</a>
</div>
<div data-role="modalview" data-height="300px"
data-width="300px"
id="myModalView">
<h2> I am a modal view... </h2>
</div>
它也可以使用 jQuery 插件语法进行初始化:
<div data-role="view" data-init="init">
<a href="#myModalView" data-rel="modalview"
data-role="button">
Open Modal View
</a>
</div>
<div id="myModalView">
<h2> Modal View... </h2>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function init(){
$('#myModalView').kendoMobileModalView({
height:100,width:100
}).data("kendoMobileModalView");
}
</script>
类型
试试 js 提琴:
http://jsfiddle . net/kenomoble/29 CDW/
http://jsfiddle . net/kenomoble/szym sp/
打开小部件
可以使用open()方法打开 ModalView 小部件。可以在打开小部件时触发一个事件,可以使用下一节示例代码中演示的data-open属性来附加该事件。
下面的代码片段打开了一个 ModalView 小部件:
<div data-role="view">
<a data-click="openModalView"
data-role="button">
Open Modal View
</a>
</div>
<div data-role="modalview"
data-height="300px"
data-width="300px"
id="myModalView">
<h2> I am a modal view... </h2>
</div>
<script>
function openModalView(e) {
$("#myModalView").data("kendoMobileModalView").open();
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/Y5WcN/
关闭小部件
可以使用close方法关闭小部件。关闭可以使用data-close属性附加的小部件时,可以触发事件。
下面的代码片段以编程方式打开一个 ModalView 小部件,并在小部件的打开和关闭过程中触发事件。在小部件里面,有一个关闭我!按钮,当点击该按钮时,使用data("kendoMobileModalView").close()方法关闭部件:
<div data-role="view">
<a data-rel="modalview"
href="#myModalView"
data-role="button">
Open Modal View
</a>
</div>
<div data-role="modalview" data-open="modalViewOpen"
data-close="modalViewClose" data-height="200px"
data-width="300px"id="myModalView">
<a style="float:right" data-click="closeModalView"
data-role="button">
Close Me !
</a>
<h2> I am a modal view... </h2>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function modalViewOpen(e){
console.log('widget opened');
}
function modalViewClose(e){
console.log('widget closed');
}
function closeModalView(e){
$("#myModalView").data("kendoMobileModalView").close();
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/6wPXA/

类型
通过设置data-modal='false',也可以通过点击窗口外部来关闭 ModalView 小部件:
$('#myModalView').kendoMobileModalView({
modal:false,
}).data("kendoMobileModalView");
拆分视图小部件
在平板设备上,与手机相比,我们有很大的空间,很少像手机那样占用整个空间来放置一个 UI 元素。SplitView 小部件允许我们使用窗格小部件将平板电脑的屏幕分成多个部分,并在每个窗格中显示不同的视图。这个小部件通常用于在左侧显示导航,在右侧显示详细视图。参见以下分割视图的经典示例——iPad设置屏幕:

与其他剑道移动小部件不同,分割视图小部件不会添加到视图中,而应该像本地或远程视图文件中的视图一样独立添加。拆分视图小部件应该只包含窗格作为子元素,视图在窗格中定义。
初始化
通过设置属性data-role="splitview"来初始化分割视图小部件,如以下代码片段所示:
<div data-role="splitview" >
<div data-role="pane" >
<div data-role="view" >
<h1>Left Pane </h1>
</div>
</div>
<div data-role="pane" >
<div data-role="view" >
<h1>Right Pane </h1>
</div>
</div>
</div>
这个例子的 jQuery 插件语法是:
<div id="kendoSplitView" >
<div data-role="pane" >
<div data-role="view" >
<h1>Left Pane </h1>
</div>
</div>
<div data-role="pane" >
<div data-role="view" >
<h1>Right Pane </h1>
</div>
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
$('#kendoSplitView').kendoMobileSplitView().data("kendoMobileSplitView");
</script>
类型
试试 js 提琴:
http://jsfiddle . net/kenomoble/tfcz 7/
http://jsfiddle . net/kenomoble/P3 HGP/
注
将style属性设置为vertical ( data-style="vertical")将使拆分视图垂直堆叠。style配置的默认值为horizontal。
拆分视图正在运行
SplitView 可以用来创建非常复杂的 UI 结构,每个窗格内有多个窗格和多个视图。让我们创建一个简单的拆分视图,作为构建复杂用户界面结构的起点。在本例中,我们创建了一个包含两个窗格的拆分视图,每个窗格中有一个视图。左侧的第一个窗格将充当主窗格,显示流派列表。当我们单击/点击其中一个列表项时,所选流派将作为视图 URL 中的查询字符串发送到详细信息/右侧窗格,并显示在详细信息视图上:
<div data-role="splitview" >
<div data-role="pane" >
<!-- Master view -->
<div data-role="view" data-title="Genre" >
<ul data-role="listview" data-style=="inset">
<li>
<a href="#view-detail?genre=Drama"
data-target="pane-detail"> Drama </a>
</li>
<li>
<a href="#view-detail?genre=Horror"
data-target="pane-detail">Horror </a>
</li>
<li>
<a href="#view-detail?genre=Family"
data-target="pane-detail">Family </a>
</li>
<li>
<a href="#view-detail?genre=History"
data-target="pane-detail">History </a>
</li>
</ul>
</div>
</div>
<div data-role="pane" id="pane-detail" >
<!-- Detail view -->
<div data-role="view" id="view-detail"
style="font-size:16px"
data-show="detailViewShown" >
<div style="padding:10px;">
<strong >Selected Genre: </strong>
<strong>
<!--Selected genre will be written here -->
<span id="selected-genre"
style="color:green"> </span>
</strong>
</div>
</div>
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function detailViewShown(e){
//Read the selected genre from the query string
$('#view-detail #selected-genre').text(e.view.params.genre);
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/G3Edm/
我们创建了一个拆分视图,并在其中定义了两个窗格。ID 为pane-detail的窗格充当详细信息窗格,在该窗格中,我们定义了 ID 为view-detail的视图。主视图窗格中的每个列表项都有一个指向详细视图的链接,在查询字符串中添加了流派,通过将属性设置为data-target="pane-detail",我们告诉窗格在窗格中搜索 ID 为pane-detail的匹配视图。
在视图的show事件中,我们正在调用函数detailViewShown()。该函数从参数集合e.view.params中可用的所选查询字符串中读取流派,并用 ID selected-genre更新范围内的文本。

注
当 SplitView 小部件中有两个窗格时,它会自动呈现,左窗格与右窗格的比例为 1:2。剑道 UI 手机 2013.1.319 版本有 bug,将比例反转为 2:1;因此,您可能必须使用该框架的更高版本来查看 SplitView 小部件的正确行为。
Scroller 小部件
滚动条小部件有助于在 DOM 中创建具有固定宽度和/或高度的触敏和可滚动部分。它可以通过在包装元素中设置data-role="scroller"属性来初始化,或者在包含视图的init事件中使用 jQuery 插件语法以编程方式进行初始化,如以下代码片段所示:
<div id="scrollableContents"> </div>
$("# scrollableContents ").kendoMobileScroller();
默认情况下,剑道视图小部件将其内容包装在 Scroller 小部件中,因此,我们应该将这个小部件仅用于视图中特定的可滚动部分。可以使用e.view.scroller属性在视图事件中访问小部件,其中e是事件的输入对象。
配置
以下是一些常用的 Scroller 配置:
useNative:当该属性设置为true时,平台上的本机滚动被启用。默认值为false。elastic:该属性在跨越 Scroller 边界时具有启用或禁用弹性行为。默认情况下,属性设置为true。zoom:使用捏缩放手势,可以放大或缩小 Scroller 小部件内的内容。默认值为false。
类型
如果本机滚动器默认支持弹性滚动,当useNative设置为true时,即使弹性设置为false,滚动也会是弹性的。
拉动刷新
拉动刷新功能内置在 Scroller 小部件中,这对于更新整个视图或逐节的内容非常有用。通过将pullToRefresh属性设置为true,可以在 Scroller 小部件中启用它。除了pullToRefresh之外,我们还可以设置以下属性来定制拉动式刷新体验:
pullOffset:当用户拉超出pullOffset值时,触发pull事件(在这个事件的处理函数中,我们编写代码来更新 Scroller 内容)。默认值为140。pullTemplate:当用户在pullOffset设置的边界内拉动滚动条时,显示 HTML 模板。releaseTemplate:当用户拉动滚动条超过pullOffset设置的边界时,会显示一个 HTML 模板。refreshTemplate:在 Scroller 中更新数据时显示的 HTML 模板。仅当调用pullHandled()方法时,此模板才会被移除。
类型
Scroller 选项只能在视图的init事件中使用 JavaScript 代码来设置,而不能以声明方式使用数据属性,因为 Scroller 小部件本身不会公开。移动视图在其内容周围实例化滚动条。
pullHandled()方法移除刷新模板中的内容,并将更新的内容定位到初始视图中。
现在让我们编写一些代码来创建一个具有拉刷新功能的 Scroller 小部件。可滚动内容包含一个数字,当用户拉动并释放滚动条时,一个介于 1 和 1000 之间的随机数将替换先前的数字:
<body>
<div data-role="view" data-init="viewInit">
<div id="scrollSection" data-zoom="true"
data-role="scroller"
style="height: 200px;text-align: center;">
<span id="scrollContent"
style="font-size: 150px;">100
</span>
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function viewInit(e) {
//get reference of the scroller
var scroller = $("#scrollSection")
.data("kendoMobileScroller");
//set scroller options
scroller.setOptions({
pullToRefresh: true,
//pull event is fired when user pulls beyond
//value set using pullOffset
pull: refresh,
//set the boundary after which the pull will
//fire the pull event default value is 140
pullOffset: 100,
pullTemplate: "<i>Pull to update the number</i>",
releaseTemplate: "Release to update the number",
refreshTemplate: "Updating..."
});
}
function refresh() {
//create a random number between 1 and 1000
var newNumber = Math.floor(
(Math.random() * 1000) + 1);
var scroller =
$("#scrollSection").data("kendoMobileScroller");
//update the contents inside the scroller
scroller.scrollElement.find('#scrollContent')
.text(newNumber);
//pullHandled method removes the contents
//in the refresh template and positions the u
//updated contents to the initial view
scroller.pullHandled();
}
</script>
</body>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/2WJhE/

滚动视图小部件
滚动视图小部件可用于使用swipe手势显示宽图像、图片库中具有多个水平页面的内容或一系列可逐页访问的指令等。可以通过将角色数据属性设置为scrollview(data-role= "scrollview")或者使用在 HTML 中调用$ ("#scrollViewMain").kendoMobileScrollView()的 jQuery 插件语法以编程方式来初始化 ScrollView 小部件,其中scrollViewMain是在其上初始化 ScrollView 小部件的 HTML 元素的 ID。
页面可以在滚动视图中定义,角色数据属性设置为“page" ( data-role= "page")。当用户浏览滚动视图时,页面中的内容会以顺序的方式一个接一个地显示。页面 HTML 元素之间的额外空白将在 ScrollView 中显示为页面,这就是为什么我们需要确保定义页面的 HTML 元素是连续的,中间没有任何空白,如以下代码所示:
<div data-role="view">
<div id="myScrollView" data-role="scrollview">
<div data-role="page">Page 1 </div><div
data-role="page">Page 2</div><div
data-role="page">Page 3</div><div
data-role="page">Page 4</div>
</div>
</div>
类型
Kendo 会自动将以下 CSS 类添加到初始化 ScrollView 的元素中:
.km-scrollview {
white-space: nowrap;
overflow: hidden;
width: 100%;
}
由于 CSS 属性white-space: nowrap,页面内部的长文本会溢出到其他页面。为了将每页中的文本包装在页面本身中,我们需要使用以下样式:
.km-scrollview > div > [data-role=page] {
white-space: pre-wrap; /*or normal*/
}
现在,让我们创建一个滚动视图,其中包含三页关于滚动视图小部件本身的提示:
<head>
<style>
.km-scrollview > div > [data-role=page] {
/*This is to preserve whitespace.
Text will wrap when necessary, and on line breaks*/
white-space: pre-wrap;
/*normal can also be used instead of pre-wrap*/
}
</style>
</head>
<body>
<div data-role="view">
<div data-role="scrollview" data-duration="3000"
data-change="pageChange"
style="padding-top: 15px;">
<div data-role="page">
<h1>Tip1</h1>
In ScrollView, remove all white spaces
between page HTML elements. Otherwise,
white spaces will appear as pages in
the rendered ScrollView.
</div><div data-role="page">
<h1>Tip2 </h1>
You can set the time taken in milliseconds
by the ScrollView to settle down the current
page after the using the duration property.
</div><div data-role="page">
<h1>Tip3 </h1>
scrollTo() method can be used to navigate
to a specific page in the ScrollView.
</div></div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function pageChange(e) {
//logs the current page number.
console.log(e.page);
}
</script>
</body>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/6JxRU/
在前面的代码中,我们在带有提示的 ScrollView 中创建了三个页面,并将data-duration属性设置为3000毫秒,这是当前页面稳定下来的动画时间。我们还将pageChange()功能作为change事件的事件处理程序,当用户导航到一个页面时会触发该事件。

类型
如果您想删除 ScrollView 底部的页面指示器,下面的 CSS 就可以了:
.km-pages > li {
display:none;
}
触摸小部件
剑道用户界面手机的触摸 小部件帮助我们使 DOM 的选定元素对用户的触摸敏感。这是一个非常强大的组件,因为我们不需要在任何需要为touch事件实现动作的地方使用移动小部件。只需在 HTML 元素上添加data-role = "touch",我们就可以捕捉诸如滑动、点击、双击、按住等事件:
<div id="touchableDiv" data-role="touch"> </div>
或者
$("#touchableDiv").kendoTouch();
现在让我们看看touch事件是如何被捕获的,以及我们可以在具有内部div元素的触摸使能div元素上提取什么样的数据。每个touch事件处理程序都会接收一个事件对象,在这个例子中,我们将探讨一些可以从事件对象中检索到的重要属性。
让我们用几个div元素创建下面的 HTML:
<div data-role="view" data-init="viewInit">
<div id="touchableDiv"
style="height: 200px; width: 200px;
background-color: darkgreen;">
<div style="height: 100px; width: 100px;
background-color: white;"
id="innerDiv">
</div>
</div>
</div>
接下来,在 JavaScript 代码中,让我们将 ID 为touchableDiv的div初始化为视图的init事件中的触摸小部件。为所有touch事件定义了单个事件处理程序,属性被写入浏览器的控制台。每个使用的属性的描述都作为注释添加到以下代码中:
<script>
var app = new kendo.mobile.Application(document.body);
function viewInit(e) {
$('#touchableDiv').kendoTouch({
tap: handleTouchEvent,
doubletap: handleTouchEvent,
hold: handleTouchEvent,
touchstart: handleTouchEvent,
touchend: handleTouchEvent,
dragstart: handleTouchEvent,
dragend: handleTouchEvent
});
}
function handleTouchEvent(e) {
console.log(e);
//Touch properties
//id of the actual HTML element
//which was touched. if we touch the div
//with id innerDiv, output will be 'innerDiv'
console.log("actual touch on: " +
e.touch.initialTouch.id);
//parent touch widget which was touched.
console.log("current target id: " +
e.touch.currentTarget.id);
//Touch offset relative to the entire document
console.log("x.location: " + e.touch.x.location +
" y.location: " + e.touch.y.location);
//Touch offset relative to the viewport
console.log("x.client: " + e.touch.x.client +
" y.client: " + e.touch.y.client);
//Velocity of the touch event in pixels
// per millisecond.
console.log("x.velocity: " + e.touch.x.velocity +
" y.velocity: " + e.touch.y.velocity);
//Epoch timestamp
console.log("x.timeStamp: " + e.touch.x.timeStamp +
" y.timeStamp: " + e.touch.y.timeStamp);
//Event Properties
console.log("Event Type: " + e.event.type);
}
</script>
</body>
类型
试试 js 提琴:
http://jsfiddle . net/kenomoble/j6xmr/
在提琴中,我们正在将输出写入 UI 中的div元素,但是如果您将输出写入您的 Chrome 浏览器控制台,您可以看到如下截图所示的输出:

刷卡事件
通过将属性设置为enableSwipe="true",可以在触摸小部件中启用水平滑动,这将触发swipe事件。对于该事件,可以配置以下属性:
maxDuration:该属性定义swipe事件的寿命,单位为毫秒。默认值设置为1秒。如果事件未在此时间范围内完成,则推送将被丢弃。minXDelta:该属性是滑动事件前需要遍历的最小水平像素。默认值设置为30。minYDelta:这是垂直方向允许的最大偏差。默认值设置为20。这意味着如果向下或向上滑动超过 20 个像素,它将被丢弃。surface:这是元素的 jQuery 对象,可以在上面扩展滑动/拖动。此属性可用于将滑动表面区域扩展到更大的父元素,从而提供更好的用户体验。
类型
当enableSwipe选项设置为true时,所有拖动事件dragstart、drag和dragend事件都将被禁用,因为它们是互斥事件。
<div data-role="view" data-init="viewInit">
<div id="parentDiv"
style="height: 300px; width: 300px;
background-color: darkgreen;">
<div style="height: 200px; width: 200px;
background-color: white;"
id="touchableDiv">
</div>
</div>
<div id="eventLog">
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
function viewInit(e){
$('#touchableDiv').kendoTouch({
enableSwipe:true,
swipe:swipeEvent,
minXDelta: 50,
maxYDelta:40,
maxDuration:2000
});
}
function swipeEvent(e){
$('#eventLog').append('Swipe registered....' +
'Direction: ' + e.direction
+ '<br/>');
}
</script>
类型
试试 js 提琴:
http://jsfiddle . net/kenomoble/R2 fyv/
之前的代码会在内部div捕捉刷卡,并在屏幕上记录刷卡方向。
多点触控手势
通过将触摸小部件的multi-touch属性设置为true,可以启用多点触摸(目前仅支持两指手势)手势。执行多点触控时触发的事件依次为gesturestart、gesturechange和gestureend。与其他触摸事件相比,这些事件的事件对象输入参数具有附加属性:
touches:包含两个活动触摸对象的数组;每次触摸一个distance:两次触摸之间的距离,单位为像素Center:两次触摸的中心点center.x和center.y提供坐标。
抽屉部件
抽屉小部件,通常被称为脸书风格的侧菜单,是在 2013 年 Q2 发布的剑道 UI 中引入的。如今,这种菜单风格已经成为任何具有大量内容和复杂导航堆栈的移动应用的标准。
通过将data-role="drawer"属性设置为div元素,可以初始化抽屉小部件。默认情况下,该小部件在从左向右滑动时从左侧出现。通过将position数据属性设置为right,可以使抽屉从右向左滑动:
data-position="right";
当我们导航到另一个视图或用户做出相反的滑动手势时,抽屉会滑回。小部件还支持添加自定义页眉页脚;此功能是可选的。
现在让我们实现一个包含ListView的抽屉,它从右向左滑动:
<div data-role="drawer" id="right-drawer"
data-position="right" >
<ul data-role="listview" data-type="group">
<li>Menu
<ul>
<li data-icon="camera">
Take Photo
</li>
<li data-icon="globe">
Browse
</li>
</ul>
</li>
<li>Account
<ul>
<li data-icon="delete">
Delete
</li>
<li data-icon="about">
About
</li>
</ul>
</li>
</ul>
</div>
<div data-role="view" id="view1"
data-title="View 1">
<div>
Swipe from right to left to
view the Drawer widget
</div>
</div>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/sCb56/
当从右向左刷时,抽屉显示在视图上,列表视图中的项目如下一组截图所示:

在特定视图上启用和禁用抽屉
抽屉部件可以配置为仅针对特定视图启用。这是通过将视图标识作为数组提供给views数据属性来实现的:
<div data-role="drawer" data-views="[view1', 'view2']">
</div>
如果未配置views数据属性,则手机 app 中所有视图都将显示微件。
类型
如果你有很多视图,只是想隐藏抽屉,只隐藏几个视图,那就去小部件的beforeShow事件,检查当前加载的视图 ID,调用preventDefault()方法不显示抽屉。
让我们修改前面的例子,看看它是如何工作的:
<div data-role="drawer" id="right-drawer"
data-position="right"
data-before-show="onBeforeShow">
<ul data-role="listview" data-type="group">
<li>Menu
<ul>
<li data-icon="camera">
<a href="#view1">Take Photo </a>
</li>
<li data-icon="globe">
<a href="#view2">Browse </a>
</li>
</ul>
</li>
</ul>
</div>
<div data-role="view" id="view1">
<div>
Swipe from right to left to
view the Drawer widget
</div>
</div>
<div data-role="view" id="view2">
<div>
Browse view in which Drawer is disabled.
</div>
</div>
<script>
var app = new kendo.mobile.Application(document.body, {
skin: 'flat'
});
function onBeforeShow(e) {
//check for view id to prevent
//display of Drawer
if (app.view().id == "#view2") {
e.preventDefault();
}
}
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/uEbgz/
在链接Take Photo和Browse的示例中,我们分别添加了两个带有标识view1和view2的视图。然后抽屉小部件的beforeShow事件被连接到事件处理程序onBeforeShow,在其中使用方法app.view().id检查当前视图的标识。如果当前视图 ID 是#view2,我们调用抽屉的preventDefault()方法,这样它就不会显示在第二个视图中。
使用导航小部件显示抽屉小部件
在前面几节中,我们看到可以使用 swipes 显示抽屉小部件。随着的滑动,我们可以使用任何一个剑道导航小部件来打开抽屉小部件。为了方便起见,我们只需要将导航元素的href属性指向抽屉的 ID,该 ID 前面加上一个#并设置data-rel="drawer",如以下脚本所示:
<div data-role="drawer" id="my-drawer">
</div>
<div data-role="view">
<a data-role="button"
href="#my-drawer"
data-rel="drawer">Open my drawer</a>
</div>
现在,让我们看一个例子,其中我们有左右抽屉小部件,带有可导航菜单和抽屉的自定义标题。两个按钮左开和右开放置在视图布局中的导航栏小部件上,可以点击打开抽屉小部件,如下代码所示:
<div data-role="layout" data-id="drawer-layout">
<header data-role="header">
<div data-role="navbar">
<a data-role="button" data-rel="drawer"
href="#left-drawer" data-align="left">
Open Left
</a>
<span data-role="view-title"></span>
<a data-role="button" data-rel="drawer"
href="#right-drawer" data-align="right">
Open Right
</a>
</div>
</header>
</div>
<div data-role="drawer" id="left-drawer"
data-views="['drawer-page1', 'drawer-page2', 'drawer-page3']">
<ul data-role="listview" data-type="group">
<li>Clickable Menu
<ul>
<li data-icon="favorites">
<a href="#drawer-page1">
Page 1
</a>
</li>
<li data-icon="downloads">
<a href="#drawer-page2"
>
Page 2</a>
</li>
<li data-icon="featured">
<a href="#drawer-page3">
Page 3</a>
</li>
</ul>
</li>
<li>Static Menu
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</li>
</ul>
</div>
<div data-role="drawer" id="right-drawer"
data-title="Two Way Menu"
data-position="right" >
<header data-role="header">
<div data-role="navbar">
<span data-role="view-title">
Two Way Menu
</span>
</div>
</header>
<ul data-role="listview">
<li >Right Item 1</li>
<li >Right Item 2</li>
<li >Right Item 3</li>
</ul>
</div>
<div data-role="view" id="drawer-page1"
data-layout="drawer-layout"
data-title="Page 1">
<h1>Page 1 </h1>
</div>
<div data-role="view" id="drawer-page2"
data-layout="drawer-layout"
data-title="Page 2">
<h1>Page 2 </h1>
</div>
<div data-role="view" id="drawer-page3"
data-layout="drawer-layout"
data-title="Page 3">
<h1>Page 3 </h1>
</div>
<script>
var app = new kendo.mobile.Application(document.body);
</script>
类型
试试 js 提琴:http://jsfiddle.net/kendomobile/sCb56/
当上面的应用加载到 iOS 6 中时,它看起来像下面的截图,在应用的导航栏两侧有两个按钮:

当点击两个按钮中的一个时,抽屉部件从各自的侧打开。从左侧打开的菜单有可点击的链接,可将您带到不同的视图。请看下面的截图:

总结
在这一章中,我们探索了更多的剑道 UI Mobile 小部件,学习了如何在多个场景中实例化和使用它们,这帮助我们创建了一个坚实的基础来完成我们的电影票应用。在下一章中,我们将学习如何在后端使用 ASP.NET 网络应用编程接口服务集成这些小部件。
七、电影票应用——完全集成
在本章中,我们将通过与剑道用户界面移动小部件、框架元素和 ASP.NET 网络应用编程接口服务集成,完成我们在第 4 章、使用框架元素开始的 MovieTickets 示例应用。我们将实现电影列表、剧院列表、购票和预告片的视图。在这一章的最后,您将真正惊讶地看到,使用 Kendo UI Mobile Framework 构建一个具有可扩展、可维护和结构良好的代码的完整端到端移动应用是多么容易!
在本章中,我们将介绍:
- 完成网络应用编程接口服务
- 前端视图
- 电影列表屏幕
- 剧院列表屏幕
- 预订机票屏幕
- 预告片视频幻灯片屏幕
完成网络应用编程接口服务
在第 3 章、带有 ASP.NET 网络 API 的服务层中,我们创建了一个名为MovieRepository.cs的存储库类,其中包含了一系列电影和影院的硬编码数据。我们将在本章中使用相同的数据来构建示例应用中的新视图。
注
该书在帕克特出版公司的网站上提供了网络应用编程接口服务和剑道用户界面手机的完整源代码。如果您不是. NET 背景或对创建服务层不感兴趣,请随意使用此 URL 中承载的服务来连接到您的前端:
http://api.kendomobilebook.com/api/
例如,要获得正在播放电影的列表,网址将是:
http://api.kendomobilebook.com/api/Movies/GetMovieList?listtype=0
完整的移动应用可在线访问:
http://movies.kendomobilebook.com
电影列表
打开文件并将以下static方法添加到MovieTicketsBLL类:
public static List<MovieBO> GetMovieList(int listType)
{
var moviesMasterList = MovieRepository.GetMoviesMasterList();
switch (listType)
{
//return now playing movies
case 0:
return moviesMasterList
.Where( m => m.IsNowPlaying == true )
.ToList();
//return coming soon movies
case 1:
return moviesMasterList
.Where(m => m.IsNowPlaying == false)
.ToList();
// return all the movies sorted by name
default:
return moviesMasterList
.OrderBy(x => x.Name)
.ToList();
}
}
该方法从存储库中读取电影列表,并根据电影类型返回列表,电影类型由输入变量listType定义:
listType = 0:返回正在播放的电影listType = 1:回归即将上映的电影
***listType = 2:以升序按字母顺序返回所有电影**
**现在让我们在MoviesController.cs中创建一个动作方法,称为GetMovieList BLL 方法:
public List<MovieBO> GetMovieList(int listType)
{
return MovieTicketsBLL.GetMovieList(listType);
}
现在我们都设置了服务方法,根据选择的过滤器在初始视图中列出电影。为确保您的服务方法有效,在构建服务后,在浏览器上点击以下网址:
http://localhost/movietickets.webapi/api/Movies/GetMovieList/?listType=0
您将得到一个响应,列出所有正在播放的电影,如下图所示:

电影预告片
该服务方法将返回电影预告片的 YouTube 链接,该链接将显示在应用的预告片视图的 ScrollView 小部件中。让我们从创建业务层开始。
在BLL\BusinessObjects目录中创建一个名为TrailerBO的类:
public class TrailerBO
{
public string MovieName { get; set; }
public string VideoUrl { get; set; }
}
储存库
现在在MovieRepository.cs类中声明一个类级static变量:
public static List<TrailerBO> trailersMasterList;
我们正在创建一个static列表,因为所有请求的存储库基础数据都是相同的,所以我们在整个应用中只需要一个实例。该变量将保存要在应用中显示的预告片列表。
现在在MovieRepository.cs类中添加以下方法:
//Method to create hardcode data for trailers
private static void CreateTrailersMasterList()
{
trailersMasterList = new List<TrailerBO>()
{
new TrailerBO(){
MovieName= "The Great Gatsby (2013)",
VideoUrl= "http://www.youtube.com/embed/rARN6agiW7o?html5=1"
},
new TrailerBO(){
MovieName= "Iron Man 3",
VideoUrl= "http://www.youtube.com/embed/2CzoSeClcw0?html5=1"
}
}
}
此方法为电影预告片创建一个 YouTube 链接列表以及电影名称。您可以在此对象中添加任意数量的 YouTube 链接;参见源代码中的这个片段:
//This method returns a list of
//trailer links and movie names
public static List<TrailerBO> GetTrailersMasterList()
{
if (trailersMasterList == null
|| trailersMasterList.Count == 0)
{
CreateTrailersMasterList();
}
return trailersMasterList;
}
如果trailersMasterList对象尚未加载数据,则该方法加载数据,并返回数据。业务层调用此方法来检索预告片列表。
业务层
在 BLL 文件MovieTicketsBLL.cs中,增加以下方法,将预告片列表返回给控制器动作方法:
public static List<TrailerBO> GetTrailers()
{
return MovieRepository.GetTrailersMasterList();
}
动作方法
现在在MoviesController.cs中,添加以下返回预告片列表的动作方法:
public List<TrailerBO> GetTrailers()
{
return MovieTicketsBLL.GetTrailers();
}
票务的动作方法
我们还需要一个控制器和一个购买门票的方法。这是一个POST方法,以一个票对象作为输入。因为我们没有数据库,所以我们不会在这个控制器方法中做任何进一步的处理,只是返回一个true值来标记售票成功。
现在在BLL\BusinessObjects目录中创建一个名为TicketPurchaseBO.cs的文件,代码如下:
public class TicketPurchaseBO
{
public string TheaterId { get; set; }
public string MovieId { get; set; }
public int NoOfChildTickets { get; set; }
public int NoOfAdultTickets { get; set; }
public string ShowDate { get; set; }
public string ShowTime { get; set; }
public string TotalAmount { get; set; }
}
现在创建一个名为TicketController的控制器,并添加一个开机自检操作:
// POST api/tickets
public bool Post(TicketPurchaseBO ticket)
{
//implement save logic here. For the sake of simplicity
//we assume that the ticket is saved properly and
//return a success boolean.
return true;
}
我们将在电影列表视图的票务工作流的最终屏幕中使用此操作方法。
这完成了我们的网络应用编程接口服务方法的设置,并允许我们构建我们的电影票应用的屏幕。
前端视图
现在让我们开始构建我们前端的两个待定部分:
- 电影列表和票务工作流程:
- 电影列表带滤镜
- 影院精选电影列表
- 订票画面
- 电影预告片
配置
让我们在configuration.js文件中创建网络应用编程接口网址,这样我们就不必在应用中使用硬编码字符串:
MovieTickets.configuration = (function () {
var serviceUrl = "http://localhost/movietickets.webapi/api/";
return {
serviceUrl: serviceUrl,
accountUrl: serviceUrl + "Account/",
getMovieListUrl: serviceUrl + "Movies/GetMovieList/",
getTheaterListForMovieUrl: serviceUrl +
"Theater/Get/",
getTrailersUrl: serviceUrl + "Movies/GetTrailers/",
purchaseTicketsUrl: serviceUrl + "Tickets"
}
})();
类型
如果要使用我们提供的托管服务,可以用以下代码片段的应用替换serviceUrl的声明:
var serviceUrl = "http://api.kendomobilebook.com/api/"
电影列表屏幕
这是显示电影列表的应用的初始视图。在现实应用中,列表可能是用户所在位置附近正在播放的所有电影。这个列表可以使用 HTML5 地理定位应用编程接口找到,或者使用这个配置文件信息找到。为了简单起见,我们只是使用支持搜索的ListView小部件来显示电影列表。屏幕顶部将有一个ButtonGroup小部件,带有三个过滤器选项,如下所示:
- 现在播放
- 即将到来
- A-Z
根据过滤器的选择,电影将被载入列表。点击电影列表项目后,下一个屏幕会显示影院列表。用户可以选择一个影院,然后点击电影时间进入下一个订票画面。这是最后一个可以购买门票并完成交易的屏幕。
从初始屏幕开始的应用工作流程如下所示:

一旦我们完成这个视图,并将其与来自服务的数据连接起来,我们的应用将看起来像下面的截图:

JavaScript 模块
让我们通过定义视图的视图模型和命名空间MovieTickets中的其他方法,为电影列表视图创建 JavaScript 模块movieList。
我们将添加从服务中获取数据的方法来加载viewModel对象,并在电影列表的类型从用户界面改变时初始化视图和事件处理程序。电影列表过滤器使用一个ButtonGroup小部件实现,包括以下选项:正在播放、即将到来和 A-Z :
MovieTickets.movieList = (function(){
var viewModel = kendo.observable({
movieList: {}
});
//fetches the list of movies from the service
//depending on the listType filter
function getMovieList(listType) {
var movieListoptions = {
url: MovieTickets.configuration.getMovieListUrl,
data: { listType: listType },
requestType: "GET",
dataType: "JSON",
callBack: callBack
};
//service call
MovieTickets.dataAccess.callService(movieListoptions);
}
//callback method from service call
function callBack(result) {
if (result.success === true) {
viewModel.set("movieList", result.data);
}
}
//this event is fired when movie list
//type is changed from the UI
function listTypeSelected(e) {
getMovieList(e.sender.selectedIndex);
}
//Loading the movie list with listType= 0
//which is Now Running list
function init(){
getMovieList(0);
}
return {
initialize: init,
getMovieList: getMovieList,
viewModel: viewModel,
listTypeSelected:listTypeSelected
}
})();
让我们来看看这段代码的一些重要部分:
getMovieList:该方法使用MovieTickets.dataAccess模块调用网络应用编程接口服务,并用callBack方法返回的数据更新viewModel对象。调用MoviesController的GetMovieList动作方法来检索电影列表。listTypeSelected:这是更换电影列表类型滤镜时触发的事件(Now Playing、Coming Soon、A-Z)。在此事件处理程序中,getMovieList方法使用选定的过滤器作为输入进行调用。init:这个方法调用getMovieList()方法,第一个过滤项现在播放作为输入。这是默认过滤器。
在 return 语句中,所有的public对象和方法都被返回,这样就可以通过在它们的名称前面加上MovieTickets.movieList来访问它们。
电影列表视图
在第 2 章、构建您的第一个移动应用中,我们定义了index.html文件中电影列表屏幕的视图。让我们从第二章中的代码开始构建用户界面组件。电影列表将使用剑道ListView小部件显示。每个列表项包含以下内容:
- 电影图像
- 电影名称
- 类型
- 评级
- 电影长度
- 主角
当我们点击的“正在播放”列表中的任何一个电影项目时,该视图应该导航到 ID 为mt-theaters-movie-view的“影院”列表视图。
列表项模板
前面的数据需要在 ListView 小部件中呈现,为此,我们需要创建一个剑道 UI 模板,如下面的代码所示:
<!--template for movie list -->
<script type="text/x-kendo-template" id="mt-main-tmpl-movie-list">
# var ecodedURI = '\\#mt-theaters-movie-view?movieId=' +
MovieId + '&movieName=' +
encodeURIComponent(Name) + '&rating=' +
Rating + '&image=' + Image #
#if(IsNowPlaying !== true){
ecodedURI= "";
}#
<a href="#:ecodedURI#" >
<img class="mt-movie-photo" src="#:Image#" />
<div class="mt-movie-details">
<span class="mt-listitem-title"> #:Name# </span>
<span data-bind="invisible:IsNowPlaying"
class="mt-highlight-label2">[Coming Soon]</span><br/>
<span class="mt-normal-label"> #:Genre#, </span>
<span class="mt-normal-label"> #:Length# Mins </span>
<span class="mt-highlight-label"> #:Rating# </span> <br/>
<span class="mt-normal-label"> #:LeadStars# </span>
</div>
</a>
</script>
该模板为电影的所有属性创建占位符,并将其包装在锚点标签周围,并设置href属性以导航到剧院列表视图(ID mt-theaters-movie-view),所选电影的所有属性作为查询字符串。
编码 URI
重定向的 URI 是使用模板中的变量encodedURI创建的。一旦 URI 被正确形成,该变量的值被设置为锚标签的href值。JavaScript 方法encodeURIComponent用于使电影名称安全地通过 URIs 传输,因为电影名称可能包含特殊字符,这可能会使 URIs 陷入混乱。
未发行的电影不应该启用导航,因为不会有要显示的影院列表,并且不能预订门票。因此,如果属性IsNowPlaying不是true,我们将ecodedURI 的值设置为空字符串,以便在点击即将到来的电影时,不会出现导航。
**### 注
我们正在设计一个电影列表项目模板,以便使用查询字符串将选定电影的详细信息传输到重定向视图,而不是在应用中共享 JavaScript 对象。实现这种方法的一个优点是,在开发过程中,一旦我们对重定向的视图进行了更改,我们就可以通过刷新浏览器来查看/调试这些更改,因为加载视图的所有数据都在 URL 中可用,而不像 JavaScript 对象那样在刷新浏览器时会丢失数据。
风格
以下样式也被添加到Styles.css文件中,以设置模板内容的样式:
.mt-movie-photo {
float:left;
height:90px;
width:70px;
}
.mt-listitem-title {
font-size:19px;
font-weight:bold;
color:darkblue;
}
.mt-highlight-label {
color:maroon;
}
.mt-highlight-label2 {
color:green;
}
配置视图和列表视图
在视图定义中,我们现在可以添加列表视图小部件,连接init事件,并设置model属性,如以下代码所示:
<!-- Movies main view --->
<div data-role="view" id="mt-home-main-view" data-title="Movies"
data-init="MovieTickets.movieList.initialize"
data-model="MovieTickets.movieList.viewModel"
data-layout="mt-main-layout" class="no-backbutton">
<!--Movie List -->
<ul id="mt-main-movie-list-view" data-role="listview"
data-template="mt-main-tmpl-movie-list"
data-filterable="{field:'Name', operator:'startsWith'}"
data-bind="source:movieList">
</ul>
</div>
在 ListView 小部件中,我们设置模板 ID,使用名字使小部件可过滤,并将数据源绑定为movieList,这是MovieTickets.movieList.viewModel的一个属性。由于我们将视图的model属性设置为MovieTickets.movieList.viewModel对象,该对象的所有属性在视图中都可用,我们可以使用它们,而无需在它们前面加上父对象的名称。在导航到应用时,我们可以看到正在播放电影的列表。

电影列表类型按钮组
如前所述,在应用中,我们有三种类型的电影列表:Now Playing、Coming Soon和A-Z。A 按钮组 是这个场景中最适合使用三种列表类型过滤电影的剑道 UI 小部件。现在,让我们在列表视图小部件上方添加一个按钮组小部件,通过添加以下代码来选择列表类型:
<ul data-role="buttongroup" data-index="0"
data-select="MovieTickets.movieList.listTypeSelected">
<li>Now Playing</li>
<li>Coming Soon</li>
<li>A-Z</li>
</ul>
选择事件绑定到movieList模块中定义的MovieTickets.movieList.listTypeSelected事件。要更改按钮组小部件的默认 iOS 特定颜色,我们可以在Styles.css中添加以下样式:
.km-ios #mt-home-main-view .km-buttongroup .km-button:not(.km-state-active) {
width:70px;
font-size: 1em;
background-color:gray;
color:white;
text-shadow:none;
}
.km-ios #mt-home-main-view .km-buttongroup .km-state-active {
width:70px;
font-size: 1em;
background-color:#FF6600;
color:white;
background-image:none;
}
先前的样式使按钮组小部件的默认背景颜色为灰色,所选按钮的背景颜色为橙色。

点击按钮组小部件的按钮,现在将加载选定电影类型的列表视图小部件。
影院列表屏幕
一旦用户通过点击电影列表屏幕中的项目来选择电影,用户被重定向到剧院列表屏幕,在该屏幕上显示具有地址和放映时间的剧院列表。
JavaScript 模块
让我们通过为视图和相关方法创建带有视图模型的 JavaScript 模块来开始构建这个屏幕。在js文件夹中创建一个名为theaters-movie-list.js的文件,并将其引用添加到index.html文件中。该模块的设计类似于电影列表屏幕:
MovieTickets.theaterListForMovie = (function () {
//ViewModel to be bound to the view
var viewModel = kendo.observable({
theaterList: {},
selectedMovie: {
movieId: "",
movieName: "",
rating: "",
imageUrl:""
},
selectedDate: ""
});
//retrieve list of theaters from the service
function getTheaterList(movieId) {
var serviceOptions =
{
url: MovieTickets.configuration
.getTheaterListForMovieUrl,
data: { movieId: movieId },
requestType: "GET",
dataType: "JSON",
callBack: callBack
};
MovieTickets.dataAccess.callService(serviceOptions);
}
function callBack(result) {
if (result.success === true) {
viewModel.set("theaterList", result.data);
}
}
//handler for show event of the view
function show(e) {
//hard coding today's date for selected date
viewModel.set('selectedDate', new
Date().toLocaleDateString());
//read the selected movie's details from the query string
viewModel.set("selectedMovie", {
movieId: e.view.params.movieId,
movieName:
decodeURIComponent(e.view.params.movieName),
rating: e.view.params.rating,
imageUrl: e.view.params.image
});
getTheaterList(e.view.params.movieId);
}
return {
show: show,
getTheaterList: getTheaterList,
viewModel: viewModel,
}
})();
让我们来看看 viewModel对象的属性和方法:
theaterList:包含有地址和放映时间的影院列表。selectedMovie:这个对象保存从电影列表视图中选择的电影的细节。此对象在视图的 show 事件中初始化,属性值从查询字符串中读取。selectedDate:该对象包含显示放映时间的日期。在我们的示例应用中,它总是当前日期。getTheaterList(movieId):这个方法从服务中获取影院列表,结果加载到viewModel对象的theaterList属性中。我们使用的是Theater控制器的Get动作方法。show(e):这个是视图中show事件的事件处理方法。集合e.view.params中可用的查询字符串值被提取并分配给selectedMovie对象的属性。调用getTheaterList()方法作为该事件处理程序的最后一步。
影院列表视图
现在让我们打开index.html和配置影院列表视图。视图的模型属性设置为MovieTickets.theaterListForMovie.viewModel,show事件连接到MovieTickets.theaterListForMovie.show。
所选电影的所有属性都使用剑道 MVVM 的data-bind属性绑定到 HTML 元素:
<div data-role="view" id="mt-theaters-movie-view"
data-model="MovieTickets.theaterListForMovie.viewModel"
data-show="MovieTickets.theaterListForMovie.show"
data-layout="mt-main-layout" data-title="Theaters">
<div id="mt-theaters-movie-details">
<img class="mt-movie-photo"
data-bind="attr: { src: selectedMovie.imageUrl}" />
<div class="mt-movie-details">
<div class="mt-listitem-title"
data-bind="text: selectedMovie.movieName"></div>
<div class="mt-highlight-label"
data-bind="text: selectedMovie.rating"></div>
Date: <span class="mt-highlight-label"
data-bind="text: selectedDate"></span>
</div>
</div>
<hr />
<!-- Theater ListView goes here -->
</div>
影院列表模板
在 影院列表视图中,我们还将使用ListView小部件,并使用剑道模板渲染列表项目。
创建一个模板,如以下代码所示:
<script type="text/x-kendo-template"
id="mt-main-tmpl-theater-list">
<!-- redirect URI-->
#var ecodedURI = 'theaterName=' +
encodeURIComponent( Name ) +
'&address=' + encodeURIComponent( Address ) +
'&movieName=' + encodeURIComponent(
MovieTickets.theaterListForMovie.viewModel.selectedMovie.movieName) +
'&rating=' + MovieTickets.theaterListForMovie.viewModel.selectedMovie.rating +
'&movieId=' + MovieTickets.theaterListForMovie.viewModel.selectedMovie.movieId +
'&theaterId=' + TheaterId +
'&date=' + encodeURIComponent(
MovieTickets.theaterListForMovie.viewModel.selectedDate )
#
<div>
<span class="mt-title"> #:Name# </span>
<span class="mt-highlight-label"> #:MilesFromCurrentLoc# Miles </span><br/>
<span class="mt-normal-label"> #:Address# </span>
<div>
<!-- render show times -->
#for(var i=0; i < Timings.length; i++) {#
<a class="mt-timings"
href="\\#BookTickets.html?#:ecodedURI#
&time=#:encodeURIComponent(Timings[i])#">
#:Timings[i]#</a>
<!--Add pipe separator for show times -->
#if(i < (Timings.length - 1)){#
|
<!--The pipe separator won't appear for the last item -->
#}#
#}#
</div>
</div>
</script>
电影的影院和放映时间由用户通过点击渲染的放映时间来选择。选择放映时间后,用户被重定向到订票视图,查询字符串中包含所选电影、影院和放映时间的详细信息。为此,我们使用模板中的变量encodedURI来创建 URI,就像在电影列表视图中所做的那样。剧院的详细信息(如剧院名称、地址和放映时间)使用模板渲染,放映时间列表用管道( | )符号分隔。
CSS 类mt-timings被添加到Styles.css中,为屏幕上出现的放映时间设置样式:
.mt-timings{
color:green !important;
text-decoration: none !important;
}
为影院添加列表视图
剧院列表视图中的最后一步是添加一个剑道列表视图小部件,并使用我们在上一节中创建的模板进行渲染。
添加以下 Kendo ListView 小部件作为视图定义中的最后一项,视图模型对象的theaterList属性绑定到列表视图作为其数据源:
<ul id="mt-theaters-movie-list" data-role="listview"
data-template="mt-main-tmpl-theater-list"
data-bind="source:theaterList">
</ul>
现在我们都设置好运行我们的应用,从初始屏幕中选择一部电影,并查看影院列表屏幕!

订票界面
我们的电影票应用票务工作流程中的最后一个屏幕是订票屏幕,用户可以在这里为成人和儿童选择和购买票。两种门票类型都有加减按钮,图标形式为加号( + )和减号( - ),由剑道 MVVM 绑定绑定,可以加减每种类型的门票数量,显示每一部分的总金额以及更新总计。
通过点击购买按钮完成购买,该按钮会将机票详情发送给服务。
JavaScript 模块
让我们在js文件夹中创建一个名为book-tickets.js的文件,并在文件中添加一个名为MovieTickets.bookTickets的 JS 模块,如下代码所示:
MovieTickets.bookTickets = (function () {
var viewModel = kendo.observable({
//method to increment no of child tickets
incrementChildTicket: function () {
this.set("noChildTickets", parseInt(
this.get("noChildTickets")) + 1);
},
//method to increment no of adult tickets
incrementAdultTicket: function () {
this.set("noAdultTickets", parseInt(
this.get("noAdultTickets")) + 1);
},
//method to decrement no of child tickets
decrementChildTicket: function () {
if (this.get("noChildTickets") !=0 )
this.set("noChildTickets", parseInt(
this.get("noChildTickets")) - 1);
},
//method to decrement no of adult tickets
decrementAdultTicket: function () {
if (this.get("noAdultTickets") != 0)
this.set("noAdultTickets", parseInt(
this.get("noAdultTickets")) - 1);
},
//get the total amount for adult ickets
noAdultTotalAmount: function () {
return this.get("noAdultTickets") * 10.00
},
//get the total amount for child tickets
noChildTotalAmount: function () {
return this.get("noChildTickets") * 8.00
},
//get total amount
totalAmount: function () {
return viewModel.noAdultTotalAmount() +
viewModel.noChildTotalAmount();
},
noAdultTickets: 0,
noChildTickets: 0,
//movie details
selectedMovie: {
movieId: "",
movieName: "",
rating: "",
},
//teater details
selectedTheater: {
theaterId: "",
theaterName: "",
address: "",
time:""
},
});
})();
viewModel对象具有以下功能:
incrementChildTicket()incrementAdultTicket()decrementChildTicket()decrementAdultTicket()noChildTotalAmount()noAdultTotalAmount()totalAmount()
这些方法的功能是不言自明的。属性noAdultTickets和noChildTickets将分别保存成人票和儿童票的总数。selectedMovie和selectedTheater酒店将装载预订的电影和剧院。
显示事件
在show事件中,viewModel对象selectedMovie和selectedTheater的属性通过从查询字符串中读取各自的值来加载。现在在我们的MovieTickets.bookTickets模块中的viewModel定义后面添加这个show事件:
function show(e) {
viewModel.set("selectedMovie", {
movieId: e.view.params.movieId,
movieName: decodeURIComponent(e.view.params.movieName),
rating: e.view.params.rating,
});
viewModel.set("selectedTheater", {
theaterId: e.view.params.theaterId,
theaterName: decodeURIComponent(e.view.params.theaterName),
address: decodeURIComponent(e.view.params.address),
time: decodeURIComponent(e.view.params.time),
date: decodeURIComponent(e.view.params.date)
});
}
购票
现在我们给MovieTickets.bookTickets模块增加一个名为purchaseTickets()的功能。在这种方法中,我们将检查用户是否至少选择了一张票据,否则会向用户显示一条警告消息。一旦票通过验证,订票所需的所有数据都以 HTTP POST 的形式发送到我们网络应用编程接口服务中的Tickets控制器。购票成功后,剑道移动模式视图小部件会向用户显示一条成功消息:
//method to purchase tickets. This method sends
//all the data to the service in an HTTP POST
function purchaseTickets() {
//no tickets are selected, display a message
if (viewModel.noAdultTickets == 0 &&
viewModel.noChildTickets == 0) {
alert('Atleast one ticket needs to be purchased.');
return;
}
var serviceOptions = {
url: MovieTickets.configuration.purchaseTicketsUrl,
data: {
movieId: viewModel.selectedMovie.movieId,
theaterId: viewModel.selectedTheater.theaterId,
noOfAdultTickets: viewModel.noAdultTickets,
noOfChildTickets: viewModel.noChildTickets,
showDate: viewModel.selectedTheater.date,
showTime: viewModel.selectedTheater.time,
totalAmount: viewModel.totalAmount
},
requestType: "POST",
dataType: "JSON",
callBack: purchaseCallBack
}
//service call.
MovieTickets.dataAccess.callService(serviceOptions);
function purchaseCallBack(result) {
if (result.success === true) {
//display a message to the user
//using modal view
var intializedModalView =
$("#mt-modal-complete").data("kendoMobileModalView");
//if the modal view is already initialized,
//destroy it
if (intializedModalView) {
intializedModalView.destroy();
}
$('#mt-modal-complete').kendoMobileModalView({
height: 130, width: 200
}).data("kendoMobileModalView").open();
} else {
alert('purchase failed. please try again.');
}
}
}
我们需要添加到这个模块的最后一个方法是在订票成功后关闭 ModalView 小部件并重定向到主页的方法:
//method to close the modal view and redirect
//to the main view.
function closeModalView() {
//close the confirmation modal popup
$("#mt-modal-complete").data("kendoMobileModalView").close();
//navigate to the home page
MovieTickets.main.getKendoApplication().navigate('#mt-home-main-view');
}
订票视图
对于 Book Tickets 视图,让我们在根文件夹中创建一个名为BookTickets.html的外部 HTML 文件,并添加一个 Kendo Mobile 视图,如以下代码片段所示:
<div data-role="view"
data-title="Book Tickets" data-layout="mt-main-layout"
data-show="MovieTickets.bookTickets.show"
data-model="MovieTickets.bookTickets.viewModel"
id="mt-book-tickets-view">
</div>
视图的标题设置为订票,视图的展示事件连线到MovieTickets.bookTickets.show 方法,MovieTickets.bookTickets.viewModel设置为视图的模型。在这个视图中,我们将添加两个部分,第一个是电影详细信息部分,我们在这里显示电影名称、等级、剧院名称、地址、日期和时间。在第二部分中,我们提供了用于添加和移除带有文本框的门票的按钮,以便用户可以选择成人和儿童的门票数量。在屏幕底部,添加了一个完成购买的按钮。为了显示电影细节部分,我们将使用一个包含两个列表项的 ListView 小部件。数据将使用 MVVM data-bind属性显示在<span>元素中:
<ul data-role="listview">
<li>
<span class="mt-listitem-title"
data-bind="text: selectedMovie.movieName">
</span>
<span class="mt-highlight-label"
data-bind="text: selectedMovie.rating">
</span>
<br />
</li>
<li>
<span class="mt-title"
data-bind="text: selectedTheater.theaterName">
</span>
<br />
<span class="mt-normal-label"
data-bind="text: selectedTheater.address">
</span>
<br />
<span class="mt-normal-label">Date:</span>
<span class="mt-highlight-label"
data-bind="text: selectedTheater.date">
</span>
<span class="mt-highlight-label"
data-bind="text: selectedTheater.time"></span>
</li>
</ul>

在第二部分中,我们将在一个列表视图小部件中添加内容。我们将在屏幕上显示成人和儿童的总金额,以及总计,并在用户更新门票计数时自动更新数字。这是通过将用户界面元素绑定到我们的可观察viewModel的属性和方法来实现的,这已经在视图定义中通过设置完成了:
data-model= "MovieTickets.bookTickets.viewModel"
现在让我们为第二部分添加 HTML:
<ul data-role="listview" data-style="inset">
<li>
<div>
Adult:
<br />
<img src="img/add.png" class="mt-list-image"
data-bind="click:incrementAdultTicket" />
<input type="text" data-bind="value:noAdultTickets"
class="mt-ticket-num-input" />
<img src="img/remove.png" class="mt-list-image"
data-bind="click:decrementAdultTicket" />
x 10.00 = $
<label data-bind="text:noAdultTotalAmount"></label>
</div>
</li>
<li>
<div>
Child:
<br />
<img src="img/add.png" class="mt-list-image"
data-bind="click:incrementChildTicket" />
<input type="text" data-bind="value:noChildTickets"
class="mt-ticket-num-input" />
<img src="img/remove.png" class="mt-list-image"
data-bind="click:decrementChildTicket" />
x 8.00 =
$<label data-bind="text:noChildTotalAmount"></label>
</div>
</li>
<li>
<div>
<span class="mt-title">Total: </span>
<span class="mt-total-amount">
$<label data-bind="text:totalAmount"></label>
</span>
</div>
</li>
<li>
<div>
<a data-role="button" class="mt-purchase-button"
data-click="MovieTickets.bookTickets.purchaseTickets">
Purchase
</a>
</div>
</li>
</ul>
在Style.css增加以下 CSS,美化 UI:
.mt-ticket-num-input {
width: 45px !important;
position: relative !important;
border: ridge !important;
min-width: 45px !important;
}
.mt-total-amount {
color:orangered;
font-size:25px;
}
.mt-list-image {
vertical-align:middle;
}
.mt-purchase-button {
background-color: orangered !important;
width:100px;
}
如果我们点击 购买按钮,门票详情会发送到服务器,如果回拨结果成功,会向用户显示一条消息。

创建预告片视频幻灯片屏幕
现在让我们创建应用的最终屏幕,它显示电影预告片的幻灯片,这些预告片是 YouTube 上托管的视频。为此,我们正在使用剑道用户界面移动滚动视图小部件。
在此视图中,从服务接收视频链接和电影名称,并将视频链接添加到剑道用户界面模板中定义的<iframe>元素中。
HTML
让我们打开第二章、创建的Trailers.html文件,并添加以下 HTML:
<div data-role="view" data-title="Trailers"
data-show="MovieTickets.trailers.show"
data-layout="mt-main-layout" id="mt-trailers-view">
<div id="mt-trailer-scrollview" data-role="scrollview">
</div>
<!-- In order for the pages to be rendered properly
make sure that you don't leave empty spaces
between template's script tags and the html contents.
-->
<script type="text/x-kendo-template"
id="mt-tmpl-trailers"><div data-role="page"
class="mt-trailer-item">
<div class="mt-listitem-title">#:MovieName# </div>
<div>
<iframe width="220" src="#:VideoUrl#"></iframe>
</div>
</div>
</script>
</div>
在前面的 HTML 中,我们定义了一个 ScrollView 小部件和 ID 为mt-tmpl-trailers的模板,用于在 ScrollView 中加载内容。剑道模板也用标识mt-tmpl-trailers定义,其中电影名称显示在顶部,视频网址嵌入在<iframe>元素中,因此视频缩略图显示在视图上。现在,我们只需要来自服务的数据来馈送模板,并在 ScrollView 小部件中分配呈现的 HTML。
JavaScript 模块
在js文件夹中创建一个名为trailers.js的文件,并在文件中添加以下模块代码:
MovieTickets.trailers = (function () {
function show(e) {
var options = {
url: MovieTickets.configuration.getTrailersUrl,
requestType: "GET",
dataType: "JSON",
callBack: callBack
};
MovieTickets.dataAccess.callService(options);
}
function callBack(result) {
if (result.success === true) {
var trailerTemplate = kendo.template(
$("#mt-trailers-view #mt-tmpl-trailers").html());
$("#mt-trailers-view #mt-trailer-scrollview")
.data('kendoMobileScrollView')
.content(kendo.render(trailerTemplate,
result.data));
}
}
return {
show:show
}
})();
现在将这个 CSS 类添加到Style.css中,使视频居中对齐:
.mt-trailer-item {
text-align:center;
}
在视图的 show 事件中,会触发一个 Ajax 调用来检索要在幻灯片放映中显示的视频列表。一旦接收到数据,就将其作为内容提供给视图中定义的模板,并动态生成预告片幻灯片。
为此,在我们的代码中,我们首先将模板的内容读入一个名为trailerTemplate的变量。然后使用kendo.render()方法,使用服务返回的数据将模板呈现为 HTML,并作为 ScrollView 小部件的内容进行馈送,如图所示:
$("#mt-trailers-view #mt-trailer-scrollview")
.data('kendoMobileScrollView')
.content(kendo.render(trailerTemplate,
result.data));
如果我们通过点击预告片标签项导航到预告片视图,我们可以看到正在显示的视频。向左或向右滑动将逐个显示电影预告片。

注
您可以访问http://movies.kendomobilebook.com查看完整的申请。
总结
在本章中,我们通过连接到使用 ASP.NET 网络应用编程接口构建的基于 HTTP 的服务,构建了处理实时数据的功能,从而完成了我们的电影票示例应用。我们学习了如何在现实应用中使用重要的剑道 UI Mobile 小部件和框架元素,并看到了使用剑道 UI Mobile 框架构建移动应用是多么容易。您现在已经准备好使用剑道用户界面移动框架构建高性能的基于 HTML5 的移动应用了!快乐编码!****















浙公网安备 33010602011771号