Kendo UI Web 开发学习指南(全)
原文:zh.annas-archive.org/md5/3ab3cfce404c21e16af4d5fcd6273d27
译者:飞龙
协议:CC BY-NC-SA 4.0
前言
今天的网络开发需要真正的 HTML5、JavaScript 和 CSS 专业知识。这些技术并非完全新颖,但围绕这种编程模型的增长如此之快,以至于在尝试创建新网站时很难找到方向。似乎每个流行的网站都有不同的、特殊的技巧来渲染吸引人的布局或在创建响应式和动态体验方面。初学者在尝试学习如何以这种方式编程时可能会感到绝望。
幸运的是,许多 JavaScript 库应运而生以满足这种强烈的需求。这些库中的大多数都通过特殊的快捷方式提供客户端功能,以便开发者可以在不编写,甚至不理解复杂的 JavaScript 代码的情况下利用非常强大的功能。jQuery 库是这方面的一个很好的例子;它们仅用几行代码就提供了丰富的功能和控制台,隐藏了下面的复杂编程。
Telerik 将此模型推进了一步。他们构建了一个强大的 JavaScript 框架,称为 Kendo UI,该框架建立在 jQuery 之上,但可以用更简单的代码创建完整的控件。不仅如此,它还包括服务器端代码库,使开发者能够在服务器上创建控件,并且所有 JavaScript 都会自动生成!这极大地提高了生产力,并使经验丰富的网络开发者和初学者能够在同一赛场上操作。本书将带您初步了解 Kendo UI 框架,并展示如何创建一套有用且强大的小部件,使您的网页像互联网上最好的网站一样闪耀。
本书涵盖内容
第一章,与数据交互:数据源、模板和网格,教授了 Kendo UI 数据源和模板 JavaScript 对象的基础知识。了解这些工具的基本知识以及所有小部件中最重要的网格。这些概念将成为您使用 Kendo UI 框架进行所有创作的基石。
第二章,自动完成小部件及其用法,展示了如何使用 Kendo UI 在文本框上创建一个单词轮或自动完成效果,以便在用户输入时显示单词建议。了解如何使用此小部件以及如何将其连接到不同的数据源。
第三章,使用和自定义日历,展示了如何使用非常少的代码在网页上创建一个功能齐全的日历控件。了解如何使用 Kendo UI 框架自定义此小部件以满足您的需求。
第四章,Kendo MVVM 框架,向您介绍了使用 Kendo UI 进行模型-视图-视图模型(MVVM)开发。JavaScript MVVM 框架是强大的系统,允许您通过声明 HTML 属性将动态数据绑定到网页上。这些系统可能很复杂,但 Kendo UI MVVM 框架可以做到尽可能简单。学习如何使用它来启用强大的动态网页。
第五章,HTML 编辑器和自定义工具,展示了 Kendo UI 编辑器小部件。这个 HTML 编辑器小部件允许您为用户提供一个有用的区域,用于使用样式和布局格式化文本输入。这对于博客、论坛和评论网站是一个完美的功能。学习如何使用和自定义这个小部件以适应您的网页。
第六章,菜单和 ListView,向您介绍了 Kendo UI 的菜单和 ListView 小部件,以便您可以在网页上有效地格式化和显示数据。菜单小部件创建了一个动态菜单,通过悬停效果打开,并允许自定义动画和行为。ListView 是一个非常灵活的小部件,允许您以任何您喜欢的方式格式化和模板数据。学习如何使用这些小部件在您的页面上显示数据。
第七章,实现 PanelBar 和 TabStrip,展示了如何在网页上构建手风琴控件和标签页。手风琴控件提供了一种在页面不增加尺寸的情况下包含大量内容的有用方式。它一次只能显示一个内容部分,同时仍然提供对其他内容的即时访问。标签页对于在页面上创建显示用户可以访问的网站其他区域的导航栏非常有用。您将学习如何使用 PanelBar 小部件创建手风琴控件,以及如何使用 TabStrip 小部件创建标签页。看看使用这些小部件如何使您的网页看起来更美观。
第八章,滑块基本知识,将教会您如何使用 Kendo UI 滑块小部件以吸引人的方式显示数字范围。这个小部件是一种非常方便的方法,可以从带有可滑动和步进移动的图形栏的网页表单中收集数值输入。学习如何将这些小部件添加到您的网页上,使您的网页表单更加出色。
第九章,实现 Splitter 和 TreeView 小部件,将展示如何在网页上布局可调整大小的内容区域,以及如何使用简单的小部件可视化层次数据。Splitter 小部件有助于将网页组织成可调整大小的区域。TreeView 小部件为层次数据创建动态显示。学习如何创建这些小部件并将它们连接到数据源。
第十章,上传和窗口小部件,提供了如何将强大的文件上传页面和交互式对话框构建到您的网站中的说明。上传小部件创建了一个功能强大的文件上传实用程序,它可以与 AJAX 一起工作,甚至允许拖放功能。窗口小部件创建模态对话框,使您的网页上的某些区域在必要时出现在其他内容之上。学习如何制作这些小部件并将它们添加到您的页面上。
第十一章,Web API 示例,介绍了您已经学到的关于 Kendo UI 小部件的知识,并带您进入使用 ASP.NET Web API 框架的更高级开发领域。Web API 为您的 Kendo UI 小部件提供了一个强大的服务器端后端,并打开了创意自定义开发的可能。学习如何使用 ASP.NET MVC 在自己的网络应用程序中管理这项技术。
您需要这本书什么
要完成这本书中的示例,您首先需要 Visual Studio 2012。如果您还没有安装,可以从www.microsoft.com/visualstudio下载 Visual Studio 的免费试用版。您还需要从 Telerik 获取的 Kendo UI Complete for ASP.NET MVC 安装包,您可以在www.kendoui.com/download.aspx获取。
这本书面向谁
这本书是为初学网络开发人员设计的,他们开始学习如何利用 JavaScript 库来创建丰富和交互式的网络应用程序。用户应熟悉 JavaScript、HTML 和 CSS。一些关于 ASP.NET MVC 的知识有帮助,但不是必需的。
习惯用法
在这本书中,您将找到许多不同风格的文本,以区分不同类型的信息。以下是一些这些风格的示例及其含义的解释。
文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称如下所示:“both标记位置选项将在滑块的两侧放置标记。”
代码块设置如下:
#stateOrTerritory {
width:200px;
}
AutoCompletePage
当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:
new StateTerritory{ Name = "Washington", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "West Virginia", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "Wisconsin", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "Wyoming", IsContiguous = true,
IsState = true, IsTerritory = false }
新术语和重要词汇以粗体显示。您在屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“请注意,Kendo UI 窗口小部件的内容尚未显示,它必须首先通过事件激活;在这种情况下,该事件是点击显示窗口按钮。”
注意
警告或重要提示将出现在这样的框中。
小贴士
小贴士和技巧如下所示。
读者反馈
我们欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢什么或可能不喜欢什么。读者反馈对我们开发您真正能从中获得最大收益的标题非常重要。
要向我们发送一般反馈,只需发送一封电子邮件到
如果您在某个主题领域有专业知识,并且您对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在您已经是 Packt 书籍的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大收益。
下载示例代码
您可以从www.packtpub.com的账户下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
错误清单
尽管我们已经尽一切努力确保我们内容的准确性,但错误仍然可能发生。如果您在我们的书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何错误清单,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击错误清单提交表单链接,并输入您的错误清单详情来报告。一旦您的错误清单得到验证,您的提交将被接受,错误清单将被上传到我们的网站,或添加到该标题的错误清单部分。任何现有的错误清单都可以通过从www.packtpub.com/support选择您的标题来查看。
盗版
互联网上版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上遇到任何我们作品的非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过
我们感谢您在保护我们的作者和我们为您提供有价值内容的能力方面的帮助。
问题
如果您在本书的任何方面遇到问题,可以通过
第一章. 与数据交互:数据源、模板、标签页和网格
今天是成为一名网页开发者的激动人心的时候。网络浏览器和网络标准已经发展到程序员现在有丰富的框架可供选择,以提升生产力,用更少的代码和更少的烦恼触及广泛的受众。HTML、CSS 和 JavaScript 已经合并成一个强大而连贯的单元,使得网络应用在美学和架构上既美丽又优雅。来自 Telerik 的 Kendo UI 是一个拥抱这些进步的现代框架,提供了一套工具,以实现丰富的网络开发和可配置的控件,所有这些都具有熟悉和易访问的语法。
沿着同样的思路,开发工具也在不断改进,Microsoft 的 Visual Studio 2012 就是一个很好的例子。JavaScript 现在是 Microsoft 世界中的第一公民,IDE 中对 JavaScript 开发的改进以及 HTML5 和 CSS3 的支持也得到了改善。这主要是为了支持 Windows 8 中的新编程模型,允许网络开发者将他们的技能带到 Windows 8 桌面,但这些改进也直接受益于 ASP.NET 开发——特别是 ASP.NET MVC。这是我们将在本书中使用的编程环境,用于演示和学习 Kendo UI 框架。
设置样本项目
Kendo UI for web development 是一个客户端、由 jQuery 驱动的 JavaScript 框架,它不依赖于任何特定的服务器技术或平台。这意味着您可以使用您选择的工具和调试/测试环境来编写和运行本书中的客户端示例。然而,Telerik 也发布了一套针对 Microsoft ASP.NET MVC 框架的服务器端扩展,这可以显著提高生产力。为了利用这两种模型,我将使用 Visual Studio 2012 和 ASP.NET MVC 4 项目模板来进行所有演示,并邀请您跟我一起学习。Visual Studio 2012 Express 可以从 www.microsoft.com/visualstudio/eng/products/visual-studio-overview 免费下载,如果您还没有安装它。
小贴士
是否想要下载完成的样本?
本书展示的样本可供下载,如果您不想自己设置所有步骤,也可以从完成的代码开始。
一旦您安装了 Visual Studio 2012,请从启动页面或从 文件 菜单中选择 新建项目。然后从项目选择中的 Web 组中选择 ASP.NET MVC 4 网络应用。如以下截图所示,我已经将我的项目命名为 LearningKendoUIWeb:
选择此选项并单击 确定。下一个窗口将显示一些关于你想要使用的模板类型的选项。我选择了基本模板,但你可以选择任何模板,除了空模板,以便跟随示例。你不需要为本书的目的创建单元测试项目。
小贴士
下载示例代码
你可以从你购买的所有 Packt 书籍的账户 www.packtpub.com 下载示例代码文件。如果你在其他地方购买了这本书,你可以访问 www.packtpub.com/support 并注册以直接将文件通过电子邮件发送给你。
Visual Studio 将为你的新项目创建文件夹结构,并将所有必要的文件复制到该结构中,以便你可以在调试器中运行你的项目。一旦完成,你将在 Visual Studio IDE 的 解决方案资源管理器 部分看到你的项目树。
现在我们有了我们的结构,是时候下载 Telerik Kendo UI 文件并将它们放在正确的位置了。导航到 Telerik Kendo UI 网站 www.kendoui.com/download.aspx,下载包含 ASP.NET MVC 服务器包装器的 Kendo UI 完整包的 30 天免费试用版。它将以 ZIP 文件的形式到达,包含你使用 Kendo UI 进行开发所需的所有内容。将 ZIP 文件的内容提取到你可以记住的地方,因为你将需要在本书的其余部分引用这些文件。此截图显示了 ZIP 文件应该包含的内容:
现在,按照以下步骤操作:
返回 Visual Studio,在 解决方案资源管理器 中的 Content 文件夹上右键单击,并选择 添加,新建文件夹。将新文件夹命名为 kendo。
右键单击你刚刚创建的 kendo 文件夹,再创建两个新的文件夹—Default 和 textures。现在,右键单击 Default 文件夹,并选择 添加,现有项。
在显示的文件对话框中,导航到解压后的 Kendo 文件夹所在的文件夹,然后打开 Styles 文件夹,接着打开其内部的 Default 文件夹。
选择此文件夹中的所有文件,然后单击 添加 按钮。这将把这些所有项目添加到 Visual Studio 项目中,以便在 解决方案资源管理器 中显示,并可以从 Visual Studio IDE 中进行管理。
接下来,按照相同的步骤将这些所有项目添加到 textures 文件夹中。一旦这些文件就位,再次在 解决方案资源管理器 中右键单击 kendo 文件夹,并选择 添加,现有项。
在显示的对话框中,从解压的 kendo 文件夹的 Styles 文件夹中选择这两个特定的文件,并将它们也添加进去:
kendo.common.min.css
kendo.default.min.css
一旦这两个文件出现在解决方案资源管理器中,通过删除文件名中的.min部分来重命名它们(kendo.default.min.css变为kendo.default.css);这将在接下来的几个段落中详细解释。当你完成时,解决方案资源管理器中的Content文件夹应该看起来像这样:
接下来,我们将按照一些非常相似的步骤准备Scripts文件夹。在解决方案资源管理器中,在Scripts文件夹内创建一个kendo文件夹,然后从下载的 Kendo 文件的js文件夹中复制以下文件:
jquery.min.css
kendo.all.min.js
kendo.aspnetmvc.min.js kendo.web.min.js
再次,删除文件名中的.min部分。然而,我们稍后将需要两个版本的kendo.aspnetmvc.js文件。现在,请复制该文件,但只从其中一个副本中删除文件名中的.min部分。这样,你将有一个带有.min文件名的文件副本,另一个没有.min文件名的文件副本。完成后的kendo文件夹在解决方案资源管理器中应该看起来像这样:
作为一名网页开发者,你肯定熟悉在网页头部引用脚本和样式的练习。ASP.NET MVC 4 自带一个很棒的功能,可以对这些脚本进行打包和压缩,同时内置缓存,以便浏览器可以更快地下载这些文件,从而在几乎不需要你做任何努力的情况下提高你网站的性能。此功能还适用于 CDN 位置,因此你可以在调试时使用本地文件,同时在网站部署时引用 CDN 托管的脚本或样式表。为了使我们的示例项目启用此功能,你需要在项目的App_Start文件夹中的BundleConfig.cs文件中添加以下代码。首先,在文件顶部添加此代码以启用 CDN 功能并保存我们想要使用的 CDN 位置的路径:
// Enable CDN
bundles.UseCdn = true;
// CDN paths for kendo stylesheet files
var kendoCommonCssPath = "http://cdn.kendostatic.com/2013.1.319/styles/kendo.common.min.css";
var kendoDefaultCssPath = "http://cdn.kendostatic.com/2013.1.319/styles/kendo.default.min.css";
// CDN paths for kendo javascript files
var kendoWebJsPath = "http://cdn.kendostatic.com/2012.2.710/js/kendo.web.min.js";
然后,在文件底部添加以下代码以创建您的 Kendo 文件捆绑包。通过将 CDN 位置作为 ScriptBundle 构造函数的第二个参数传递,Visual Studio 将在调试时使用您本地的文件构建解决方案,并在发布模式下使用 CDN 位置的文件构建解决方案。这也是我应该解释为什么我们移除了 JavaScript 和样式表文件名中的 .min 部分。ASP.NET MVC 的捆绑和压缩功能在调试期间有意忽略了文件名中包含 .min 的文件。这意味着在调试期间,您的 Kendo 下载中的所有脚本引用都不会工作,因为我们项目中没有包含预压缩的文件。互联网上有几种处理此问题的方法,但对我们项目来说,最简单的方法就是重命名文件以避免整个问题。
// Create the CDN bundles for kendo javascript files
bundles.Add(new ScriptBundle("~/bundles/kendo/web/js", kendoWebJsPath)
.Include("~/Scripts/kendo/kendo.web.js"));
// The ASP.NET MVC script file is not available from the Kendo Static CDN,
// so we will include the bundle reference without the CDN path.
bundles.Add(new ScriptBundle("~/bundles/kendo/mvc/js")
.Include("~/Scripts/kendo/kendo.aspnetmvc.js"));
// Create the CDN bundles for the kendo styleshseet files
bundles.Add(new StyleBundle("~/bundles/kendo/common/css", kendoCommonCssPath)
.Include("~/Content/kendo/kendo.common.css"));
bundles.Add(new StyleBundle("~/bundles/kendo/default/css", kendoDefaultCssPath)
.Include("~/Content/kendo/kendo.default.css"));
现在我们已经正确配置了 BundleConfig.cs 文件,我们可以调整 _Layout.cshtml 文件头部部分的引用。_Layout.cshtml 文件通过为所有页面创建统一的头部结构以及一个默认布局,在其中所有其他页面放置其特定内容,充当我们的默认母版页。在 Views、Shared 文件夹中打开 _Layout.cshtml 文件并做一些修改。默认情况下,它将包含一些出现在页面主体部分的脚本引用,以及一些出现在头部部分的。
虽然无疑有很好的理由这样做,但由于在这些脚本引用出现之前,我们页面的主体中已经有了 Kendo 脚本的引用,因此我们需要将所有内容移动到头部部分。由于这个文件不是很长,我已经在这里包含了我的完成版本,以便您可以复制它:
@using Kendo.Mvc.UI;
@Styles.Render("~/Content/css")
@Styles.Render("~/bundles/kendo/common/css")
@Styles.Render("~/bundles/kendo/default/css")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/kendo/web/js")
@Scripts.Render("~/bundles/kendo/mvc/js")
@RenderBody()
@RenderSection("scripts", required: false)
注意,我还已经在文件的顶部添加了一个 @using 语句,请确保也复制它,因为它将启用所有页面的 Intellisense。Intellisense 是 Visual Studio 的一个功能,可以在您编写代码时自动完成代码,并且是一个极大的生产力提升器。要完全启用此功能,您还需要将 Kendo.Mvc.dll 文件添加到您的 Visual Studio 项目中:
首先,在 Visual Studio 的 Solution Explorer 中右键单击 LearningKendoUIWeb 项目,并选择 添加引用。
接下来,单击 浏览 并在文件对话框中导航到您下载 Kendo 文件的位置。
找到名为 aspnetmvc 的文件夹,打开其中名为 Binaries 的文件夹,然后打开该文件夹内名为 Mvc3 的文件夹。
在这里您可以找到 Kendo.Mvc.dll 文件;单击它并选择 添加。
添加此引用后,您可以通过在名为 web.config 的文件中添加一个特殊条目,使该代码中的所有内容对您的所有网页可用。
此文件位于你的LearningKendoUIWeb项目的根目录中。打开web.config文件,找到名为namespaces的部分。将Kendo.Web.UI命名空间添加到列表中,如下所示:
现在在项目中创建一个文件夹来存放静态内容。在解决方案资源管理器中右键单击项目名称,选择添加,新建文件夹。将新文件夹命名为static。这将是我们放置所有除 MVC 框架之外客户端示例的位置。
Visual Studio 2012 在 JavaScript Intellisense 方面有一些很好的改进,这将帮助我们编写代码。在脚本文件夹中打开名为"_references.js"的文件,并删除其中的所有文本。这是我的"_references.js"文件的全部内容,将其复制到你的文件中:
///
///
///
Visual Studio 2012 使用此文件作为编辑器中 Intellisense 应使用的 JavaScript 库列表。我已经包含了 Kendo 压缩包中包含的 jQuery 文件以及我们将在大多数网页中使用的两个 JavaScript 文件。一旦你设置好这些,你将在 JavaScript 文件中获得一些非常有帮助的编码辅助,如下所示:
注意当你开始在编辑器中输入 JavaScript 代码时,所有的 Kendo 选项是如何显示出来的?当你在这本书的示例中编程时,这将变成帮助你的一项技能。
好的,现在我们准备好了!
KendoUI 语法风格
当你在网页中使用 KendoUI 框架时,你会发现有两种方法可以将小部件添加到你的内容中。标准方法是使用类似这样的 jQuery 语法在脚本元素中:
$("#makeMeADatePicker").kendoDatePicker();
如所示,惯例是先通过 jQuery 选择元素,然后从 Kendo 命名空间中应用一个 JavaScript 方法,将内容转换为交互式的 Kendo UI 小部件。
现在还有一种方法,通过 HTML5 提供,可以通过称为声明性初始化的方法将 Kendo UI 小部件添加到你的内容中。这是一种通常添加以"data-"开头的特殊属性到你的元素中的实践,然后调用一个初始化器,该初始化器读取这些属性并应用适当的更改。以下代码是一个示例:
kendo.init($("#makeMeADatePicker"));
这种语法允许 JavaScript 和标记之间有更清晰的分离,这在我们在书中稍后要介绍的 MVVM 模式中非常重要。它也非常强大且表达力丰富,可以使代码更易于阅读,因为相关的属性直接包含在它们相关的元素中。包含代码的脚本块不一定出现在实际受影响的代码旁边,这可能会在复杂的项目中使追踪变得困难。
Kendo UI MVC – 基础
由于本书将大量使用 ASP.NET MVC,因此我应该定义一些重要术语,以避免以后产生混淆。MVC代表模型-视图-控制器;让我们围绕这些术语建立一个共同的理解。首先,一个网页被称为视图,当使用 C#的 Razor 语法时,网页有一个文件扩展名,cshtml。还有使用 Visual Basic 的选项,在这种情况下,网页有一个文件扩展名,vbhtml,但本书我们将使用 C#,所以你不会在示例中看到这一点。
其次,控制器是一个服务器端类文件,负责生成网页(视图)中包含的所有逻辑。控制器,连同路由表,还负责建立公开可访问的 URL,服务器将对此做出响应,并强制执行访问它们所需的 HTTP 动词。一般来说,控制器负责联系任何外部依赖项,如数据库或 Web 服务器,对从这些外部依赖项检索的数据执行任何必要的逻辑和计算,然后将所有处理过的数据打包成一个称为模型的对象。
然后,模型是一个对象容器,包含网页(视图)需要的数据,以便显示自己。在一个正确分离的系统里,控制器是执行所有逻辑、数据处理、用户输入处理、授权和安全的引擎。视图是数据展示者,只关心所提供数据的图形表示;除了展示所需的逻辑之外(不排除展示可能很复杂),不涉及任何其他逻辑。模型是控制器用来将其最终产品发送到视图进行展示的标准数据格式。
当在 ASP.NET MVC 环境中编程时,Kendo UI 提供了一套丰富的服务器端扩展,用于创建其小部件。你不需要编写 HTML 元素,指定其属性并将其连接到 Kendo UI JavaScript,整个过程可以使用出现在视图中的服务器端对象来完成。例如,在 MVC Razor 语法中创建一个DatePicker小部件看起来像这样:
@(Html.Kendo().DatePicker().Name("datePickerField"))
没有 HTML,没有 JavaScript,只有 HTML 类的扩展方法。然而,当页面生成时,你可以看到发送到浏览器的内容:
jQuery(function(){jQuery("#datePicker").kendoDatePicker({format:"M/d/yyyy",
min:new Date(1900,0,1,0,0,0,0),max:new Date(2099,11,31,0,0,0,0)});});
扩展方法会动态创建所有 HTML、JavaScript 和 CSS 信息。你可以看到最终输出如何使用 jQuery 方法选择输入元素,并使用.kendoDatePicker(…)通过 JavaScript 创建小部件。因此,尽管程序员没有编写 JavaScript,但 Kendo UI 仍然需要它;MVC 扩展只是正常 Kendo UI 客户端框架的包装器。
我还应该解释,尽管视图是生成发送到用户浏览器的最终网页的部分,但它首先在服务器上处理。Razor 语法(以@开头的一切)永远不会出现在最终的页面标记中,它是在服务器上处理的,以便生成最终的标记。这意味着 Kendo MVC 扩展方法实际上是在服务器端创建最终标记的快捷方式,以便它们像在 JavaScript 中那样正常工作。
在 MVC 框架中编程允许在 Web 服务器内部实现非常清晰的关注点分离,这反过来又允许在视图的运行方式和它们对服务器端逻辑的依赖性方面有很大的灵活性。例如,使用数据的控件可以从视图本身接收这些数据(对服务器端逻辑的依赖),或者它们可以通过调用返回 JSON 的动作方法从客户端查询数据(对服务器端逻辑的依赖较少)。
作为服务器依赖实现的一个示例,这里有一个包含嵌入式模型数据的强类型视图,然后可以由页面上的控件使用。强类型视图是一个指定包含其模型数据的特定类型对象的视图页面。您可以在示例的第一行看到强类型模型对象,它以@model开头:
@model IEnumerable
@Html.Raw(ViewBag.serverData)
varserverData = eval($("#serverData").html());
for (var i = 0; i console.log(serverData[i].Name); } ViewBag是一个动态对象,在控制器动作方法和视图页面上可用。它是一个字典对象,可以包含您在视图页面上需要的任何数据或对象。控制器可以添加任何您需要添加到ViewBag中的内容,然后您的视图页面将能够访问这些数据或对象,就像这个示例代码所展示的那样。在这种情况下,控制器附加了一个名为serverData的对象,其中包含其模型数据的 JSON 表示。我们使用名为eval()的 JavaScript 函数将其解析为 JavaScript 对象,然后在 JavaScript 控制台上显示其内容。这仅仅是一个示例,说明如何将数据嵌入到视图本身,而无需使用额外的网络请求,例如 jQuery 函数$.get或$.ajax来检索显示在页面上的数据;在某些情况下,这可能是有益的,因为需要权衡网络流量和服务器可以预先提供的即时数据可用性。 ViewBag.serverData属性在控制器中填充如下: publicActionResultAutoCompletePage() { var repository = new SampleRepository(); var data = repository.GetStatesAndTerritories(); ViewBag.serverData = new JavaScriptSerializer().Serialize(data); return View(data); } 注意,在这个示例中,控制器既填充了这个ViewBag属性,又将相同的数据作为强类型模型发送到视图;这不是必需的,但在这里很有用,因为我们可以利用服务器的JavaScriptSerializer类在我们将其发送到视图之前为我们创建 JSON。以下是当我们用具有名称属性的数组对象的 JSON 表示填充ViewBag.serverData时,JavaScript 控制台显示的内容: 从单独的端点请求数据,然后在获取后使用它的情况要常见得多。这允许使用来自外部源的数据,并打破了服务器在页面内部提供数据的依赖性,这意味着特定的服务器实现可能不太重要且更简单。jQuery 提供了几种常见且友好的方式来检索 JSON 数据,如$.ajax、$.get和$.getJSON。Kendo 也通过其小部件的配置选项提供标准方式来检索外部数据,通常通过transport.read属性方法。我们将在本章的其余部分以及本书的其余部分中看到更多关于此的内容,当我们讨论 DataSource 和 Grid 时。 管理数据 Kendo UI 框架由两部分组成——框架组件和用户界面(UI)小部件。本书中我们将涵盖的大部分内容与用户界面小部件及其使用方法相关,但我们将从如何在 Kendo UI 中管理数据这个重要主题开始。DataSource 组件和模板提供了一个良好的起点,并将为我们在这本书的其余部分使用的基础。 模板 Kendo UI 模板是包含一小部分页面标记的脚本块,这些标记由其他 Kendo UI 小部件用于显示重复内容。我们将首先介绍这些内容,因为它们将在我们接下来的所有示例中使用。以下是一个简单模板的示例: var template = kendo.template("#= horseColor #"); $("#horseDiv").html(template({ horseColor: 'brown})); 运行此代码会将horseDiv的 HTML 内容设置为包含传递到模板函数对象中的horseColor值的 span 元素。它将产生以下代码输出:
模板也可以在特殊类型的 HTML 脚本块中编写,这样它们的布局在 HTML 内容中看起来更自然。
在这个模板示例中,请注意包含代码片段#= variable_name #的行。这些表示由 Kendo UI 模板引擎解释的代码部分。当模板被使用时,这些代码块内的变量名被提供给模板。在模板内部使用的 JavaScript 属性名需要在调用模板时传递给模板的对象上的属性。请注意,脚本类型是x-kendo-template而不是javascript,这是很重要的,这样浏览器就不会尝试自行执行脚本块。以下是一个代码示例,展示了如何在 JavaScript 中初始化此模板:
var template = kendo.template($("#template").html());
functionshowMovies() {
$("#moviesTable").html(template(
{rank: 1, rating: 9.2, title: 'Prometheus', year: 2012}
));
}
showMovies();
注意模板是通过调用 kendo.template() 来创建的。这个方法接受字面模板代码作为其参数,这就是为什么示例中显示了调用 jQuery 语句 $("#template").html(),因为这段代码返回了模板脚本块在网页中出现的字面内容。因此,在这个例子中,它等同于调用 kendo.template('
当模板对象作为方法被调用时,它需要传入的数据作为参数。当上面的示例代码运行时,它会产生以下输出:
| 1 | 9.2 | Prometheus | 2012 |
模板还可以包含 JavaScript,这使得执行更高级的操作成为可能,例如遍历数组并为数组中的每个项目单独渲染模板。在这种情况下,你需要向模板提供一个对象数组而不是之前的一个单独的对象。这次,使用显式的参数名 data 是至关重要的。注意 JavaScript 代码被单 # 符号包围,如 # javascript code #,变量语句被 #= 和 # 包围,如 #= variable statement #。还要注意 # 符号和内容之间的空格很重要。
# for(vari=0; i
#= data[i].rank #
#= data[i].rating #
#= data[i].title #
#= data[i].year #
# } #
模板是构建功能 Kendo UI 小部件的重要组成部分,当与 DataSources 和 Grids 结合使用时,它们将变得更加有用,正如我们稍后将要看到的。
DataSource
Kendo UI DataSource 是一个 JavaScript 对象,为数据提供了各种 Kendo UI 小部件的共同接口。关于 DataSource 对象的完整文档可以在 Kendo UI 网站上的此地址找到:docs.kendoui.com/api/framework/datasource。DataSource 是一个相当复杂的对象,依赖于一些需要单独解释的构建块。这些构建块是 Kendo 对象,称为 Schema、Transport 和 Model。让我们首先处理这些,然后再继续探索 DataSource 本身。
重要的是要注意,在创建 DataSource 对象时,你应该使用 new 关键字来实例化一个新对象,而不是仅仅使用对象字面量:
var dataSource = new kendo.data.DataSource({...
模型
模型对象来自命名空间 kendo.data.Model,并继承自 Kendo 的 ObservableObject。它为 DataSource 使用的提供了一种已知结构,或模型,同时也可以用来启用一些更高级的功能,例如变更跟踪。要创建一个新的模型,你必须通过方法 kendo.data.Model.define() 来实现。在这个方法中,你需要传递一个对象来定义模型的结构,并设置数据元素的可配置选项。以下是一个模型的示例:
var Service = kendo.data.Model.define( {
id: "serviceId", // the identifier of the model
fields: {
"serviceName": {
type: "string"
},
"unitPrice": {
type: "number"
},
"serviceId": {
type: "number"
}
}
});
var currentService = new Service( {
serviceName: "Rotate Tires",
unitPrice: 29.95,
serviceId: 400
});
console.log(currentService.get("serviceName")); // outputs "Rotate Tires"
console.log(currentService.get("unitPrice")); // outputs 29.95
在这个示例中,我们创建了一个具有三个属性的模型,并为每个属性设置了数据类型。然后,我们根据模型定义创建了一个新的模型对象,并演示了如何通过 model.get() 方法访问其属性。我们刚刚演示了模型对象的 ID 是通过名为 id 的属性定义的,字段是通过名为 fields 的属性定义的。在 fields 属性中,这些是可以设置以配置每个数据元素的选项:
fields: {
"serviceName": { // Property name for a field
type: "string", // "string"(default), "number", "boolean", or "date"
defaultValue: "Inspection", // Default value for field when model is
/ created. Default for string is "", number
// is 0, and date is new Date() (.i.e. today)
editable: true, // Specifies whether field is editable
nullable: false, // Specifies if default value should be used when empty
parse: function(){...} // Specifies custom parser for field value
validation: {...} // Specifies the validation options used by Kendo
// Validator such as 'required', 'min', and 'max'.
},...
}
这些并非都是必需的,但在你需要一个非常具体的配置时它们是可用的。以下是从 Kendo UI 网站上的一个示例:
var Product = kendo.data.Model.define( {
id: "id", // the identifier is the "id" field (declared below)
fields: {
/* name of the field */
name: {
type: "string", // the field is a string
validation: { // validation rules
required: true // the field is required
},
defaultValue: "
},
/* name of the field */ price: {
type: "number", // the field is a number
validation: { // validation rules
required: true, // the field is required
min: 1 // the minimum value is 1
},
defaultValue: 99.99 // default field value
},
/* name of the field */ id: {
editable: false, // this field is not editable
nullable: true // a default value will not be assigned
}
}
});
由于模型中的属性是可观察的,你需要使用特殊的获取器和设置器方法来正确触发其他函数和对象观察到的行为。要检索这些属性之一的当前值,请使用 model_name.get(),例如 currentService.get('unitPrice')。要设置属性的值并因此更改它,请使用 model_name.set(),例如 currentService.set('unitPrice', 14.95)。可观察对象的概念是 MVVM 框架的一个关键特性,我们将在后面的章节中介绍。
模型对象上可用的另外两种方法是 isNew 和 toJSON。isNew 方法检查模型是否为新的。这是通过检查 id 字段是否仍然设置为默认值来确定的。如果 id 字段没有设置为默认值,则模型对象不被视为新的。toJSON 方法返回模型属性和值的完整 JSON 表示。
正如我提到的,模型继承自 ObservableObject,它公开了三个你可以附加自定义行为的事件——change、get 和 set。这些的语法是使用 model.bind(),后面跟着事件名称和处理该事件的函数:
currentService.bind('change', function(e){
alert(e.field + " just changed its value to " +
currentService.get([e.field]));
});
Schema
DataSource 中的 schema 对象负责描述原始数据格式。它在模型之上工作,甚至可以包含模型定义。Schema 的任务是指导 DataSource 在原始数据对象中查找有关错误、聚合、数据、分组和总记录的信息。这些信息作为 schema 对象中的属性存在,如下所示:
schema: {
errors: function(response) {
return response.errors;
},
aggregates: function(response) {
return response.aggregates;
},
data: function(response) {
return response.data;
},
total: function(response) {
return response.totalCount;
}
}
在前面的代码示例中,每个属性都设置为函数,当传递原始数据对象时,将返回该对象内的适当数据。这些属性也可以设置为文本字段,在这种情况下,给定的字段名必须存在于对象的顶层,并且已经包含适当格式的适当数据:
schema: {
errors: "errors_field_name",
aggregates: "aggregates_field_name",
data: "data_field_name",
total: "total_field_name"
}
aggregates属性需要以对象格式返回数据,其结构类似于以下示例。aggregates对象中的每个属性名都可以包含有关其聚合值的信息,例如最大值(max)、最小值(min)或总计数:
{
unitPrice: { // Field Name
max: 100, // Aggregate function and value
min: 1 // Aggregate function and value
},
productName: { // Field Name
count: 42 // Aggregate function and value
}
}
在这种情况下,数据在unitPrice字段上定义了最大值和最小值,在productName字段上定义了计数。数据源对象没有计算这些值;相反,它们已经存在于从远程服务器发送的原始数据中,并且模式已经向数据源对象指示了它们的位置。可以使用函数来计算聚合值,但通常原始数据已经包含由远程服务器返回的这些值。
如我之前所说,模式可以包含一个模型定义。如果是这种情况,数据源将为你调用kendo.data.Model.define来处理模型定义,以便从原始数据中创建模型对象:
var dataSource = new kendo.data.DataSource({
schema: {
model: {
id: "ProductID",
fields: {
ProductID: {
editable: false,
nullable: true
},
...
如果你已经定义了一个模型定义,你可以简单地引用它,数据源将像使用它一样使用它:
vardataSource = new kendo.data.DataSource({
schema: {
model: Product // Use the existing Product model
}
});
schema对象有一个parse属性,你可以将其设置为在处理原始数据之前将被调用的函数。这给你提供了一个机会,如果你需要的话,进行任何预处理。还有一个type属性,可以设置为json或xml。
传输
transport对象包含数据源可以使用它来与远程服务器通信以执行其任何正常数据功能的属性。这些数据功能是create、destroy、read和update(对应于可以在记录上执行的不同操作)。这些数据功能作为transport对象内的属性对象存在,并遵循相同的配置选项模式。我应该指出,并非所有数据功能都是必需的;只有那些需要在你的transport对象中定义的功能才需要定义。这是transport对象的基本配置结构。
transport: {
create: { // this sets configuration for creating new records
// on the remote server
},
destroy: { // this sets configuration for deleting records
// on the remote server
},
read: { // this sets configuration for reading records
// from the remote server
},
update: { // this sets configuration for updating records
// on the remote server
},
autoSync: false, // set true to automatically sync all changes
batch: false // set true to enable batch mode
}
这里是配置传输对象的远程数据操作的不同的选项。这四个属性遵循相同的配置模式,但在本代码示例中,我展示了不同的配置方式以供参考。在这个第一个代码示例中,我配置了创建操作,仅将 HTML 元素的值发送到远程服务器。
create: { // for creating data records on remote source.
url: "/orders/create", // the url to the create service.
data: { // data to send to the create service as part of the request.
// this can also be specified as a function call.
orderId: $("#input").val()
},
cache: true, // make false to force fresh requests every time.
contentType: "application/json", // default is
// "application/w-www-form-urlencoded"
dataType: "json", // "jsonp" is also common.
type: "POST" // which http verb to use.
}
在这个示例中,我们已将销毁方法设置为使用 jQuery $.ajax函数将数据发送到远程服务器,而不是直接在销毁配置对象上配置它。如果你更喜欢 jQuery 语法并希望轻松地将回调函数附加到操作的结果,你可以这样做。
destroy: { // same options as "create", with some alternatives shown.
// this is how you use $.ajax() to run this remote service call.
// this option can be used with "create", "destroy", "read",
// and "update"
$.ajax( {
url: "/orders/destroy",
data: options.data, // the data field contains paging, sorting,
// filtering, and grouping data
success: function(result) {
// notify the DataSource that the operation is complete
Options.success(result);
}
});
}
在这个例子中,我们创建了一个函数作为读取操作的数据源。如果您需要在接收到远程数据之前执行一些自定义逻辑,或者由于某些原因需要完全绕过远程数据源,这可能很有用。
read: { // same options as above in "create" and "destroy".
data: function() { // this is how you specify data as a function.
return {
id: 42,
name: "John Doe"
};
}
}
请记住,您刚才看到的配置选项对任何传输操作都有效,我只是简单地展示了每个配置的不同操作作为示例。当 DataSource 配置了这样的传输配置时,它将使用这些选项中的属性和函数来执行相关操作。当它加载数据时将调用read,当记录被更改时将调用update,当记录被删除时将调用destroy,当添加新记录时将调用create。
其他 DataSource 属性
当从本地数据读取时,您需要通过使用名为data的属性来引用它,如下所示:
var someData = [{ title: 'Prometheus', year: 2012, rating: 9, rank: 25 }];
var dataSource = new kendo.data.DataSource({
data: someData
});
我们尚未看到的 DataSource 的一些其他属性更多用于数据处理——aggregate、filter、group、sort、page和pageSize。它们可以在客户端数据上工作,或者可以通过使用serverAggregates、serverFiltering、serverGrouping、serverSorting和serverPaging属性来请求服务器执行操作,通过将这些属性添加到 DataSource 对象属性列表并将它们设置为 true。
aggregate属性接受一个字段名和聚合函数名的数组:
aggregate: [{ field: 'title', aggregate: 'count' }]
filter属性可以接受一个简单的对象、简单对象的数组,或者一个具有更多逻辑的可配置对象,以指定应在数据上执行筛选:
// simple object
filter: { field: 'title', operator: 'startswith', value: 'Shawshank' }
// ...or array...
filter: [{field: 'year', operator: 'eq', value: '1998'}, {field: ...
// ...or configurable object...
filter:{
logic: "or",
filters: [
{ field: 'title', operator: 'startswith', value: 'Shawshank' }]
}
这些是可以与筛选对象一起使用的不同运算符。它们也可以在通过使用serverFiltering属性向服务器请求筛选时使用。
等于: eq, ==, isequalto, equals, equalto, equal
不等式: neq, !=, isnotequalto, notequals, notequalto, notequal, ne
小于: lt, <, islessthan, lessthan, less
小于或等于: lte, <=, islessthanorequalto, lessthanequal, le
大于: gt, >, isgreaterthan, greaterthan, greater
大于或等于: gte, >=, isgreaterthanorequalto, greaterthanequal, ge
以...开头: startswith
以...结尾: endswith
包含: contains
group和sort属性可以接受一个对象或对象数组来指定分组:
group: { field: 'year', dir: 'asc' }
sort: { field: 'title', dir: 'desc' }
page和pageSize属性都接受数字来分别表示页码和每页的记录数。
DataSource 方法
DataSource 方法用于更改或检索 DataSource 对象的某些元素。其中一些与刚刚讨论过的相同数据操作属性相关——aggregate、aggregates、filter、group、page、pageSize 和 sort。在这些情况下,不传递参数调用方法将返回 DataSource 中同名属性的当前值;调用带有参数值的方法将设置 DataSource 中同名属性的值为传入的新值:
// get the current group descriptors
var g = dataSource.group();
// set a new value for filtering
dataSource.filter({ field: 'year', operator: 'gt', value: 1990 });
此外,还有添加和删除记录的方法。add 和 insert 方法都向 DataSource 添加一个新记录。add 方法简单地接受一个模型对象或与 DataSource 中的项目当前数据格式匹配的对象字面量。insert 方法接受与 add 相同的对象,但还指定一个 index 属性,指示插入新记录的零基位置。remove 方法接受一个模型对象并将其从 DataSource 中删除:
// add a new item
dataSource.add({ year: 1997, title: 'The Fifth Element', rating: 10 });
// insert an item at the 6th position in the DataSource
dataSource.insert(5, {year: 1995, title: 'Twelve Monkeys', rating 9.5});
// remove an item from the DataSource
var movie = dataSource.at(5);
dataSource.remove(movie);
at、get 和 getByUid 方法用于从 DataSource 中检索特定记录:
// get the 3rd item in the DataSource
var movie = dataSource.at(2);
// get the model instance with an id of 5
// (id is determined by the value of the schema.model.id property)
var movie = dataSource.get(5);
// get the model instance, or ObservableObject if no model has been set
// uid is a property inherited from ObservableObject
varuid = $("tr").data("uid");
var movie = dataSource.getByUid(uid);
fetch、query、read、sync、cancelChanges 和 view 方法用于管理 DataSource 的当前内容和结构:
// fetches data using the current filter/sort/group/paging information.
// will fetch data from transport if data is not already available in memory.
dataSource.fetch(); // can optionally take a callback function which
// is executed once the data is ready.
// executes a query over the data (i.e. paging/sorting/filtering/grouping)
// this effects what the call to dataSource.view() will return.
dataSource.query({ page: 5, pageSize: 20, group:{field:'year',dir:'asc'}});
// read data into the DataSource using the transport.read setting
dataSource.read(); // also conveniently causes the change event to fire
// synchronizes changes through the transport for any pending CRUD operations.
// if batch mode is enabled, it uses only one call per operation type (create,
// read, update, destroy)
dataSource.sync();
// discards all un-synced changes made to the DataSource
dataSource.cancelChanges();
// returns the current state of the items in the DataSource with all applied
//settings such as paging, sorting, filtering, and grouping.
// to ensure that data is available, this method should be used from
//within the change event of the DataSource
change: function(e){
...
kendo.render(template, dataSource.view());
}
为了完成列表,我们将查看 data、total 和 totalPages:
// retrieve an observable array of items (the current data within the DataSource)
var movies = dataSource.data();
// set the DataSource to some new data
datSource.data([{year: 2009, title: 'Cargo', rating: 6.8}, {year: ... ]);
// get, but not set, the total number of items in the DataSource
var total = dataSource.total();
// get, but not set, the total number of pages of items in the DataSource
var pages = dataSource.totalPages();
重要的是要注意,你必须调用 dataSource.read() 以使 DataSource 对象启动读取过程并自行填充数据。换句话说,在你调用 dataSource.read() 之前,你的 DataSource 中没有任何可读取的内容。
DataSource 事件
DataSource 对象上有三个可用的事件——change、error 和 requestStart。当数据从传输中更改或读取时,会触发 change 事件。当数据读取或数据同步过程中发生错误时,会触发 error 事件;如果 DataSource 中的 schema.errors 已设置,并且来自服务器操作的响应包含由 schema.errors 指定的字段中的数据,也会触发 error 事件。当数据请求即将开始时,会触发 requestStart 事件。与其他事件一样,这些事件可以作为 DataSource 定义的一部分设置,或者稍后通过 bind 方法设置。
// set event handler as part of DataSource definition
var dataSource = new kendo.data.DataSource({
change: function(e){
// handle event
}
});
// or set event handler later through the bind method
dataSource.bind("error", function(e){
// handle event
});
正如你稍后将会看到的,更改事件是一个很好的地方,可以在 DataSource 读取新记录时生成标记。它也是放置任何其他应该对 DataSource 中的更改做出响应的代码的适当位置。
开始使用基本用法
既然我们已经看到了 DataSource 内部组件的定义,我们将组合我们的第一个示例页面,以演示在 JavaScript 中使用 DataSource 对象的基本用法。向项目的静态文件夹中添加一个新的 HTML 文件,并将其命名为 DataSource.html。首先添加以下代码:
| Rank | Rating | Title | Year |
|---|---|---|---|
我们已经在页面的头部引用了 jQuery 和 Kendo UI Web JavaScript 文件。现在让我们在div标签之后添加一个模板块,以便我们可以编写脚本以创建额外的表格行:
现在我们需要的是能够获取一些数据,并使用由这个模板定义的布局来填写表格,即使用数据源。在您刚才输入的模板脚本块之后添加以下代码:
$(document).ready(function() {
// create a template using the above definition
var template = kendo.template($("#template").html());
var movies = [
{ "rank": 1, "rating": 9.2, "year": 1994,
"title": "The Shawshank Redemption" },
{ "rank": 2, "rating": 9.2, "year": 1972,
"title": "The Godfather" },
{ "rank": 3, "rating": 9, "year": 1974,
"title": "The Godfather: Part II" },
{ "rank": 4, "rating": 8.9, "year": 1966,
"title": "Il buono, ilbrutto, ilcattivo." },
{ "rank": 5, "rating": 8.9, "year": 1994,
"title": "Pulp Fiction" },
{ "rank": 6, "rating": 8.9, "year": 1957,
"title": "12 Angry Men" },
{ "rank": 7, "rating": 8.9, "year": 1993,
"title": "Schindler's List" },
{ "rank": 8, "rating": 8.8, "year": 1975,
"title": "One Flew Over the Cuckoo's Nest" },
{ "rank": 9, "rating": 8.8, "year": 2010,
"title": "Inception" },
{ "rank": 10, "rating": 8.8, "year": 2008,
"title": "The Dark Knight" }
];
var dataSource = new kendo.data.DataSource({
data: movies,
change: function () {
// subscribe to the CHANGE event of the data source
$("#movies tbody").html(
kendo.render(template, this.view())); // populate the table
}
});
// read data from the "movies" array
dataSource.read();
});
让我们逐步分析这段代码。你应该能认出前面几行,其中创建了一个 Kendo 模板,这个模板是从你刚才输入的脚本块中创建的。之后,你将看到一个包含关于各种电影数据的 JavaScript 对象数组。这个数组将成为下一个数据源对象的原始数据。数据源对象被实例化(注意new关键字)到名为dataSource的变量中。它将movies数组作为其数据参数,并定义了一个处理数据源对象change事件的函数。在这个change事件内部,我们使用 jQuery 选择movies表格,然后使用kendo.render()从我们的template变量为dataSource对象中的每个项目生成标记。注意我们使用的模板不需要特殊的 JavaScript 来迭代集合;数据源对象通过this.view()将所有数据传递给change事件。最后,我们调用dataSource.read()来读取数据,从而触发change事件,将内容添加到我们的movies表格中。
kendo.render()方法将其第一个参数作为模板函数,第二个参数作为数据数组。它将数据通过模板运行,生成结果标记并返回给调用者。在上面的例子中,我们使用了 jQuery 将
元素的 HTML 设置为kendo.render()函数的结果。绑定到远程数据
我们上一个例子是演示如何使用本地数据(一个 JavaScript 数组)与数据源结合使用。使用数据源与远程系统上存在的数据进行操作也是非常常见且重要的。为了模拟这种情况,我们将转向 ASP.NET MVC 框架来创建一个用于我们远程数据的服务器。
在 Visual Studio 的解决方案资源管理器窗口中,右键单击控制器文件夹,选择添加,控制器。将新控制器命名为KendoController,并保持打开的对话框的其余部分为默认设置。
新创建的控制器类将出现在 Visual Studio 的编辑器部分,你将看到文件中有一个通用的Index()方法。这个方法被称为动作方法,用于处理返回给网页浏览器的 HTML 响应。上面的注释表明了用于定位此动作方法的路由和 HTTP 动词:
// GET: /Kendo/
public ActionResult Index()
{
return View();
}
在这种情况下,它表明键入路由"Kendo",如http://
在KendoController顶部添加一个用于我们即将创建的命名空间LearningKendoUIWeb.Repository的using语句。还要添加Kendo.Mvc.UI和Kendo.Mvc.Extensions:
添加一个名为RemoteData的新动作方法,并按照以下方式设置它:
publicJsonResultRemoteData()
{
var repository = new SampleRepository();
var data = repository.GetAllMovies();
returnJson(result, JsonRequestBehavior.AllowGet);
}
这是一个简单的实例化存储库(我们将在接下来的时刻创建它),从该存储库中收集一些数据,然后将它作为 JSON 返回给客户端的方法。Json()方法的第二个参数通知控制器类,即使动词是 GET,也可以从这个方法返回 JSON 数据。
右键单击Models文件夹,然后单击添加,类。将新类命名为Movie.cs。这是一个非常简单的类,用于存储有关电影的数据:
namespace LearningKendoUIWeb.Models
{
public class Movie
{
public int Rank { get; set; }
public double Rating { get; set; }
public int Year { get; set; }
public string Title { get; set; }
}
}
向项目中添加一个新文件夹,并将其命名为Repository。向此文件夹添加一个名为SampleRepository.cs的类:
using LearningKendoUIWeb.Models;
namespaceLearningKendoUIWeb.Repository
{
public class SampleRepository
{
public List
{
var movies = new List
new Movie { Rank = 1, Rating = 9.2,
Title = "The Shawshank Redemption", Year = 1994 },
new Movie { Rank = 2, Rating = 9.1,
Title = "The Godfather", Year = 1974 }
};
Return movies;
}
}
}
随意添加更多电影到这个列表,越多越好。现在我们有一个简单的存储库类,可以返回电影对象的列表,因此我们在KendoController中创建的动作方法最终是有效的。当调用RemoteData动作方法时,它将以如下所示的 JSON 对象数组形式返回电影对象列表:
我已经向我的存储库添加了更多电影,但结果的结构是相同的。这正是 DataSource 知道如何使用的数据类型。以下是连接 DataSource 以使用它的方法,在RemoteData.cshtml文件中找到创建dataSource变量的 JavaScript 代码行,并更改代码,使其看起来像这样:
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: 'Kendo/RemoteData/'
}
},
change: function () {
$("#movies tbody").html(kendo.render(template, this.view()));
}
});
我们不是使用data属性指向一个本地可用的对象数组,而是使用transport属性告诉 Kendo 我们需要从远程源请求数据。在这种情况下,我们只指定了 DataSource 如何读取远程数据,这就是我们所需要的,因为我们对 DataSource 的唯一方法调用就在这一行:
dataSource.read();
这些例子只是触及了表面,但它确实展示了 DataSource 在实际页面上的应用。然而,真正独立演示 DataSource 对象是很困难的。实际上,DataSource 只有在它服务于数据丰富的控件时才有用,比如 Kendo UI Grid。在接下来的页面中,我们将探讨这个 Grid 控件,并能够展示一个 Grid 可以充分利用的更完全配置的 DataSource。我们还将看到如何在视图页面中通过 MVC Razor 语法配置 Grid 和 DataSource。
页面布局
现在我们已经讨论了 Kendo UI 框架的 DataSource 和 Template 功能,我们可以将注意力转向那些在网页上提供图形元素的控件。其中一些控件实际上帮助您组织页面上的内容或数据,Grid 就是一个很好的例子,我们将在下一部分进行介绍。
Grid
Kendo UI Grid 是一个非常实用的控件,需要熟悉。它是一种简单的方法,可以将数据转换成可使用和交互式的表格,这在通常需要完整的服务器控件(如 ASP.NET WebForms)或页面标记中的复杂且耗时的 JavaScript 开发中是常见的。实际上,设置一个简单的示例非常容易。假设我们有一些如下所示的 JavaScript 数据,我们想在网页中显示:
var repairs = [{
name: "State Inspection",
price: 39.75,
labor: 1,
staff: 1
},
{
name: "Brake & Clutch System Service",
price: 149.95,
labor: 3,
staff: 1
},
{
name: "Power Steering Service",
price: 109.96,
labor: 3,
staff: 1
},
{
name: "Cooling System Service",
price: 126.95,
labor: 2,
staff: 1
},
{
name: "Oil Change",
price: 37.77,
labor: 1,
staff: 1
},
{
name: "CV Axle Replacement",
price: 271.11,
labor: 5,
staff: 2
},
{
name: "Battery Cabling Replacement",
price: 179.97,
labor: 2,
staff: 1
},
{
name: "Battery Replacement",
price: 118.38,
labor: 1,
staff: 1
},
{
name: "Fuel Induction Service",
price: 168.88,
labor: 3,
staff: 2
},
{
name: "Engine Air Filter Replacement",
price: 36.63,
labor: 1,
staff: 1
},
{
name: "Timing Belt Replacement",
price: 221.75,
labor: 3,
staff: 2
},
{
name: "Drive Belt Replacement",
price: 194.79,
labor: 3,
staff: 2
}
];
为了将其转换成一个格式良好的动态表格,通常需要一些循环和 HTML 标记生成,可能通过 jQuery 实现。然而,通过 Kendo UI,我们只需要创建一个 kendoGrid() 函数,我们就可以看到一些魔法在行动。注意,创建一个从这些数据中生成的表格所涉及的代码是多么少:
$("#repairsGrid").kendoGrid({
dataSource: repairs
});
下面是这段简单代码的页面输出:
看看相关的代码是否甚至不需要在网页中存在表格?Kendo UI 生成了它需要的一切,以便在页面上以表格的形式显示这些数据。现在我们可以将注意力转向创建更互动和智能的表格,并探索 Kendo UI Grid 控件在显示来自不同来源的数据方面能提供什么。
列
首先,我们可以通过在 columns 对象数组上指定属性来控制 Grid 的格式化。这个对象数组用于指示 Grid 如何适当地显示数据,以便它在页面上看起来像你想要的那样。以下是一个使用我们刚才看到的 Grid 的列对象示例,展示了可用于格式化的各种选项:
$("#repairsGrid").kendoGrid({
...
columns: [{
field: "name",
title: "Service",
width: 300
},
{
field: "price",
title: "Price",
width: 50,
format: "${0:##.##}"
},
{
field: "labor",
title: "Labor",
width: 50,
template: "#= labor# hour(s)"
},
{
field: "staff",
title: "Staff",
width: 50,
template: "#= staff # tech(s)"
}]
这里是输出效果:
还有一些简单的选项,通过指定哪些列是可筛选或可排序的,来启用一些动态交互行为。请注意,这些选项只有在 Grid 作为整体将 pageable 和/或 sortable 设置为 true 时才有用。
$("#repairsGrid").kendoGrid({
...
columns: [{
field: "name",
title: "Service",
width: 300,
sortable: true,
filterable: true
},
{
field: "price",
title: "Price",
width: 50,
format: "${0:##.##}",
sortable: true,
filterable: true
},
{
field: "labor",
title: "Labor",
width: 50,
template: "#= labor # hour(s)",
sortable: true,
filterable: true
},
{
field: "staff",
title: "Staff",
width: 50,
template: "#= staff # tech(s)",
sortable: false,
filterable: false
}],
sortable: true,
filterable: true
注意以下截图,服务列是如何按字母顺序排序的,我已经点击了过滤器图标,这使我能够对要在页面上显示的数据输入过滤器。您可以看到过滤器图标位于屏幕上打开窗口的上方,它看起来像一个小漏斗。Kendo UI 通过我们在网格上设置的dataSource属性来处理实际的排序和过滤。这意味着您在提供给网格的dataSource上设置的设置将被网格用于排序和过滤:
当网格配置为允许编辑数据时,columns属性允许您指定一个自定义编辑函数,该函数可以在更改该列中的数据时使用。这是一种为用户提供更简单的方式来输入更改,甚至控制可以进行的更改类型的有用方式。例如,此更新的代码示例显示了向Labor列添加编辑函数,以便在编辑时显示下拉列表,为用户提供一组特定的选项进行选择。这里还有一些其他更改,我们将在下一节中讨论:
$("#repairsGrid").kendoGrid({
dataSource: repairs,
columns: [
{
title: "Action",
width: 75,
command: ["edit"]
},
{
field: "name",
title: "Service",
width: 300,
sortable: true,
filterable: true
},
{
field: "price",
title: "Price",
width: 50,
format: "${0:##.##}",
sortable: true,
filterable: true
},
{
field: "labor",
title: "Labor",
width: 50,
template: "#= labor # hour(s)",
sortable: true,
filterable: true,
editor: function (container, options) {
varselectEditor = $("");
selectEditor.append(new Option("1", 1));
selectEditor.append(new Option("2", 2));
selectEditor.append(new Option("3", 3));
selectEditor.append(new Option("4", 4));
selectEditor.append(new Option("5", 5));
selectEditor.appendTo(container);
}
},
{
field: "staff",
title: "Staff",
width: 50,
template: "#= staff # tech(s)",
sortable: false,
filterable: false
}],
sortable: true,
filterable: true,
editable: "inline"
这是editor函数的输出,显示了当行进入编辑模式时出现的下拉列表。设置
当使用此类自定义编辑函数时,传入的container和options对象具有一些特定的属性,这些属性在您编写函数时可能对您有所帮助。container对象是您应该添加任何新标记的页面元素,正如我们在示例中所做的那样。options对象包含两个属性:options.field和options.model。options.field属性包含您应该在新的标记中使用的字段名称,以便 Kendo 可以正确绑定一切。options.model属性包含对在dataSource中指定的实际数据模型的引用(如果指定了的话);这使您能够访问在创建自定义逻辑时可能很重要的数据。
在代码示例中出现的其他更改包括在网格定义上的editable: "inline"属性(这是编辑功能正常工作所必需的;inline的替代方案是popup,它打开一个特殊窗口以编辑记录),以及包含命令按钮的新列。列对象的命令属性接受一个命令按钮数组,用于在每一行中生成。此数组可用的选项包括edit、create、destroy、save和cancel。当我们更详细地介绍如何将网格绑定到 CRUD 操作时,我们将很快回到这个话题。
注意,要向网格添加这些命令按钮,只需指定列对象的 command 属性即可。我没有向列添加任何
大部分网格功能可以通过描述网格当前能力的属性来启用。每个属性都以 -able 结尾。这些属性是 editable、filterable、groupable、navigatable、pageable、scrollable、selectable 和 sortable。我们已经看到了 filterable 和 sortable,并且当使用时它们接受简单的真/假值。我们也看到了 editable,但这个选项还有更多可以做的:
...
editable: {
confirmation: "Are you sure?", // text displayed to confirm a delete operation
destroy: true, // whether or not to delete item when button is clicked
mode: "popup", // options are "incell", "inline", and "popup"
template: "#= ... #", // template to use for pop-up editing
update: true // switch item to edit mode when clicked?
}
groupable 属性允许用户通过将列拖动到屏幕顶部来对列进行分组。groupable 选项还包括一个属性 groupable.messages.empty,它将在网格的空分组区域中显示。如果您指定此 messages 属性,则假定 groupable: true 值,并且不需要指定。navigatable 属性在网格内打开或关闭键盘导航。以下是我们的网格定义底部在 groupable 和 navigatable 启用时的外观:
...
sortable: true,
filterable: true,
editable: "inline",
navigatable: true,
groupable: {
messages: {
empty: "Drag column header here for grouping"
}
}
使用这些选项渲染时的页面输出:
pageable 选项可以简单地设置为 true/false,就像其他几个选项一样,但如果您需要更精细的控制,它也允许这样做:
...
pageable: {
pageSize: 10,
previousNext: true, // show buttons navigating to first/last/next/previous
numeric: true, // show numeric portion of the pager in the Grid?
buttonCount: 10, // number of buttons to show in numeric pager
input: true, // create input element allowing user to navigate to page
pageSizes: [5,10,20], //array of page size choices for user
refresh: true, // show a refresh button in the Grid?
info: true, // show a label with current paging information in it
messages: {
display: "Detail Template – {1} of {2} items", // info text
empty: "No Records", // text to show when there are no records
page: "Page", // first part of text of input option
of: "of Detail Template", // last part of text of input option
itemsPerPage: "items per page", // text for selecting page size
first: "Go to first page", // text of first page button tooltip
previous: "Go to the previous page", // previous page tooltip
next: "Go to next page", // next page tooltip
last: "Go to the last page", // last page tooltip
refresh: "Refresh" // text of refresh button tooltip
}
}
我们为每页 10 个项目配置的示例代码将看起来像这样:
...
sortable: true,
filterable: true,
editable: "inline",
navigatable: true,
groupable: {
messages: {
empty: "Drag column header here for grouping"
}
},
pageable: {
pageSize: 10
}
使用这些选项生成的输出:
scrollable 属性配置网格内部是否可以有一个垂直滚动条,通常在您在页面上限制了网格的高度时指定。它可以设置为简单的布尔值 true/false。
selectable 属性指示在网格内是否启用或禁用选择。它的可能值是 row、cell、multiple, row 和 multiple, cell。以下是我们的示例网格在 selectable: "multiple, cell" 时的外观。
注意,我已经选择了部分行进行显示。
toolbar 属性为网格启用一个具有一组命令的工具栏,类似于列对象的命令属性。toolbar 对象数组中的每个工具栏都可以配置 name、template 和 text:
...
toolbar: [
"create",
{ name: "save", text: "Save This Record" },
{ name: "cancel", text: "Cancel Changes: }]
注意工具栏可以是一个简单的文本值,指示要执行哪个命令。您还可以指定包含所需配置数据的对象(如前面的截图所示)。请参考以下截图:
摘要
在本章中,我们涵盖了大量的基础知识,以便您能够正确地开始创建启用 Kendo UI 的网页。理解如何使用模板以及如何使用数据源对于在 Kendo UI 框架中做很多事情至关重要。在这些之后,网格是 Kendo UI 框架的一个基本组件,了解如何配置它将在构建需要向用户显示表格数据的页面时给您带来先机。
在下一章中,我们将学习关于自动完成小部件的内容。它允许您向输入文本框添加一个单词轮效果,以帮助用户输入可以从数据源中查找的信息。这是一个许多用户都会被吸引的工具,它将为您的网页添加很多功能,而无需在编写代码上花费太多精力。
第二章. 自动完成小部件及其用法
Kendo UI 中的自动完成小部件在用户输入时会在输入框上创建“单词轮”效果。单词轮是一种效果,当用户输入时,单词会出现在文本框下方,帮助建议可能的搜索词。你经常在像 Google 和 Bing 这样的搜索引擎上看到这种效果。这可以用来给用户一个他们可以选择的批准选项列表,或者它也可以帮助用户准确输入特定的关键词,因为项目的规范形式会直接出现在输入框下方供用户选择。如果用户只需要输入可能的长搜索词中的一个或两个字符,这也可以节省用户的时间。Kendo UI 中的自动完成小部件非常容易配置,并且几乎不需要你做任何工作就能将此功能带给你的网站用户。
自动完成小部件 – 基础
打开上一章中创建的 Visual Studio 项目,然后打开Controllers文件夹中的KendoController.cs类。让我们为我们的初始自动完成测试页面添加一个新的操作方法。
public ActionResult AutoCompletePage()
{
return View();
}
右键单击操作方法的名称,然后选择添加视图。现在,选择对话框中出现的默认值,这将带您进入网页,以便我们可以开始操作。
将自动完成绑定到本地源
由于我们正在引用上一章中创建的默认布局,因此我们不需要重新输入任何内容就可以在我们的页面标题中获取所有需要的 Kendo 和 jQuery 文件。
我们将首先通过 JavaScript 和本地数据绑定来演示如何使用自动完成小部件。我们需要一个输入元素、一个 JavaScript 数组和一些 jQuery。
#stateOrTerritory {
width:200px;
}
AutoCompletePage
varstatesAndTerritories = ["Alabama",
"Alaska",
"American Samoa",
"Arizona",
"Arkansas",
...
"Washington",
"West Virginia",
"Wisconsin",
"Wyoming"];
$("#stateOrTerritory").kendoAutoComplete({
dataSource: statesAndTerritories,
filter: "startswith",
placeholder: "Choose state or territory...",
separator: ", "
});
我们可以使用美国和领土的列表来展示字母表,并且列表足够长,可以进行演示。我们到目前为止所做的一切就是为自动完成创建了一些 JavaScript 数据,然后使用 jQuery 和 Kendo UI 将其连接到页面顶部的输入元素。我们指定了我们想使用statesAndTerritories JavaScript 数组作为我们的数据源,我们想要在输入元素中使用占位符文本,并且数组中的项由逗号分隔。这些属性在章节末尾有更详细的解释。运行它,你应该能在浏览器中看到一个带有一些看起来不错的占位符文本的输入框。在它里面输入一些字母,你会立即得到一些州和领土的建议。
将自动完成绑定到远程数据
现在我们已经看到了如何使用本地 JavaScript 数据来配置 AutoComplete 小部件,接下来让我们看看如何使用远程数据来实现。在 Visual Studio 项目中,将名为StateTerritory.cs的新类添加到Models文件夹中。将其结构设计为包含关于州和地区的相关数据,以便我们可以在我们的页面上使用它。
namespace LearningKendoUIWeb.Models
{
public class StateTerritory
{
public string Name { get; set; }
public bool IsState { get; set; }
public bool IsTerritory { get; set; }
public bool IsContiguous { get; set; }
}
}
现在打开SampleRepository.cs类文件,并添加一些逻辑来创建我们的州和地区数据仓库。请注意,我故意将哥伦比亚特区既视为一个州又视为一个地区,以便于未来的示例。
public List
{
var stateTerritories = new List
new StateTerritory{ Name = "Alabama", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "Alaska", IsContiguous = false,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "American Samoa", IsContiguous = false,
IsState = false, IsTerritory = false },
new StateTerritory{ Name = "Arizona", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "Arkansas", IsContiguous = true,
IsState = true, IsTerritory = false },
...
new StateTerritory{ Name = "Washington", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "West Virginia", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "Wisconsin", IsContiguous = true,
IsState = true, IsTerritory = false },
new StateTerritory{ Name = "Wyoming", IsContiguous = true,
IsState = true, IsTerritory = false }
};
return stateTerritories;
}
现在我们有一些可以在服务器端操作的数据,但我们仍然需要通过 HTTP 公开它。回到KendoController.cs类文件,并添加一个新动作方法,就像你在这个代码块中看到的那样:
public JsonResult AutoCompleteData()
{
var repository = new SampleRepository();
var data = repository.GetStatesAndTerritories();
returnJson(data, JsonRequestBehavior.AllowGet);
}
这将暴露我们的州和地区集合,作为StateTerritory对象的 JSON 数组。请记住设置JsonRequestBehavior.AllowGet属性,否则这不会工作。现在我们可以修改我们的AutoCompletePage.cshtml文件,使用传输属性来获取其数据。
$("#stateOrTerritory").kendoAutoComplete({
dataSource: {
transport: {
read: {
url: "/Kendo/AutoCompleteData/"
}
}
},
dataTextField: "Name",
filter: "startswith",
placeholder: "Choose state or territory...",
});
再次运行页面,并观察它从服务器获取数据。我们必须指定哪个字段包含数据文本字段,因为我们的 JSON 数据是结构为对象,而不是简单的数组。我们也不再需要分隔符属性。
使用模型通过 MVC 使用 AutoComplete
我们可以将这一步更进一步,将我们的代码转换为 Razor 语法与 MVC 一起使用。首先,创建一个控制器动作方法,从服务器返回数据。
public ActionResult AutoCompletePage()
{
var repository = new SampleRepository();
var data = repository.GetStatesAndTerritories();
return View(data);
}
接下来,打开AutoCompletePage.cshtml文件,并在
标签之后删除所有内容。在文件顶部,我们需要添加一个声明,以便这个视图页面成为我们的新模型类StateTerritory.cs的强类型。
@model IEnumerable
现在添加这段代码,它利用 HTML 辅助类和 Kendo 扩展方法。
AutoCompletePage
@(Html.Kendo().AutoComplete()
.Name("statesAndTerritories")
.DataTextField("Name")
.BindTo(Model)
.Filter("startswith")
.Placeholder("Choose state or territory...")
)
识别出语法了吗?除了BindTo(Model)语句外,这些方法名与我们在 JavaScript 中使用的属性名相同(当然,它们的首字母都是大写的)。BindTo(Model)的调用是 MVC 控制器将数据传递到 MVC 视图的方式。在这种情况下,我们将视图强类型化为StateTerritory对象的集合(IEnumerable),在这里的代码中,我们告诉 Kendo 框架这个模型包含要在 AutoComplete 中显示的数据。这个模型中的数据在服务器创建页面时使用,并且只能通过视图页面中的 Razor 语法代码语句访问。
尽管我们刚刚通过 MVC 将 AutoComplete 连接到服务器,但我们使用的方法并不真正像是对远程数据的调用。实际上,它是通过将服务器端的所有模型数据保存到 AutoComplete 初始化的 JavaScript 中来使用本地数据。只要我们不尝试将如此多的数据嵌入到页面中以至于它加载缓慢,这并没有什么问题。实际上,在某些情况下,这可能是提高性能的好方法,但了解数据的位置以及页面如何访问它很重要。如果我们想让页面从 URL 请求数据,我们需要对我们的视图做一些修改。
通过 Ajax 使用 AutoComplete 与 MVC 结合
打开视图并修改 AutoComplete() 扩展方法调用,就像这个代码块一样。
AutoCompletePage
@(Html.Kendo().AutoComplete()
.Name("statesAndTerritories")
.Placeholder("Choose state or territory...")
.DataTextField("Name")
.Filter("startswith")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("AutoCompleteData", "Kendo");
})
.ServerFiltering(false);
})
)
我们已经移除了 BindTo(Model) 并用 DataSource() 调用替换,我们在这里使用 lambda 表达式来定义如何创建数据源。在这种情况下,我们已将其配置为使用之前配置的返回 JSON 数据的动作方法,并且服务器没有执行任何过滤。这实际上将我们的网页设置得与原始使用 transport 属性从服务器获取 JSON 数据的 JavaScript 页面相同。
向服务器发送数据
我们希望能够在服务器端过滤 AutoComplete 小部件的数据。我们可以在 transport JavaScript 对象中的 data 属性中使用,或者我们可以继续使用我们的 MVC 示例,并在 DataSource lambda 表达式中指定要发送的数据。比如说,如果我们想能够选择在 AutoComplete 小部件中显示哪些州和领地类型,我们可以通过在请求中发送一些数据来实现这一点,然后让服务器修改它发送回的数据。用以下更新后的代码替换 AutoCompletePage.cshtml 的内容。
AutoCompletePage
@(Html.Kendo().AutoComplete()
.Name("statesAndTerritories")
.Placeholder("Choose state or territory...")
.DataTextField("Name")
.Filter("startswith")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("AutoCompleteData", "Kendo")
.Data("onAdditionalData");
})
.ServerFiltering(false);
})
)
var autocomplete = $("#statesAndTerritories").data("kendoAutoComplete");
functiononAdditionalData() {
return {
showStates: $("#stateType").val()
}
}
注意 lambda 表达式中的 Data("onAdditionalData") 调用和同名的新的 JavaScript 方法 onAdditionalData()。当读取 AutoComplete 的数据源时,它将触发这个 JavaScript 事件,并将带有 showStates 参数的结果发送到服务器。为了将此数据接收进你的动作方法,你需要添加一个具有匹配名称的参数。
public JsonResult AutoCompleteData(bool showStates = true)
{
// setting showStates = true means that it is an optional parameter
// with the default value of true.
var repository = new SampleRepository();
var data = repository.GetStatesAndTerritories();
if (!showStates)
{
data = data.Where(s =>s.IsState == false).ToList();
}
return Json(data, JsonRequestBehavior.AllowGet);
}
现在,当 AutoComplete 代码将其 showStates 参数作为其网络请求的一部分发送时,控制器将使用该值来确定是否过滤它发送回的数据。
使用模板自定义 AutoComplete
Kendo 模板可以用来自定义 AutoComplete 中项目的显示外观。这甚至可以变得相当复杂,包括图片和特殊样式。这里有一个简单的例子。将 AutoCompletePage.cshtml 页面的 Razor 部分更新为如下所示:
@{
var template = "#= Name # - #= IsState #";
}
@(Html.Kendo().AutoComplete()
.Name("statesAndTerritories")
.Placeholder("Choose state or territory...")
.DataTextField("Name")
.Filter("startswith")
.DataSource(source =>
{
source.Read(read =>
{
read.Action("AutoCompleteData", "Kendo")
.Data("onAdditionalData");
})
.ServerFiltering(false);
})
.Template(template)
)
在这里,我们添加了一个名为template的变量,它包含我们的 Kendo 模板定义。然后我们在AutoComplete()设置代码中引用了它。运行页面并查看结果。
配置所有自动完成属性
自动完成小部件在初始化期间可以设置几个不同的属性来定制其行为。以下是一个结构化代码示例,展示了自动完成小部件上可用的功能。
$("autocomplete").kendoAutoComplete({
dataSource: dataSource, // see chapter 1
animation: {
close: {
effects: "fadeOut",
duration: 300,
hide: true,
show: false
},
open: {
effects: "fadeIn",
duration: 300,
show: true
}
},
dataTextField: "Name", // name of field in data source to display
delay: 500, // milliseconds before auto complete activates
enable: true, // set "false" to disable
filter: "contains", // type of filtration, passed to remote source
height: 200, // height of drop-down list
highlightFirst: true, // highlight first item in list?
ignoreCase: true,
minLength: 1, // minimum characters before activating drop-down list
placeHolder: "Enter value...", // placeholder text
separator: ", ", // separator for completion of search terms.
//allows for multiple search terms by using comma or
// other delimiter
suggest: false, // auto-type rest of search term?
template: template // see chapter 1
}
钩入自动完成小部件事件
自动完成小部件在执行页面上的操作时将触发几个不同的事件。您可以在自动完成小部件初始化后绑定它们,如下所示:
varautoComplete = $("#autoComplete").data("kendoAutoComplete");
$("#autoComplete").data("kendoAutoComplete").bind("change", function(e) {
// handle event
});
或者,您可以在自动完成小部件本身的属性中定义它们,如下所示:
$("#autoComplete").kendoAutoComplete({
close: function(e) {
// handle event
}
});
在任何情况下,这都是您可以附加您自己的代码的事件列表。
更改
当自动完成小部件中的选择更改时,将触发change事件。您可以在初始化后绑定事件。
关闭
当从自动完成小部件关闭下拉列表时,将触发close事件。
打开
open事件在每次从自动完成小部件打开下拉列表时都会触发。
选择
当从自动完成小部件中选择任何元素时,将触发select事件。它将e.item参数传递给处理它的函数,以便您可以访问所选择的项。
使用 API 自动完成方法
要从 JavaScript 代码内部访问自动完成小部件,您可以通过以下方式通过 jQuery 调用 API:
var autocomplete = $("autocomplete").data("kendoAutoComplete");
一旦您有了自动完成小部件的变量引用,您就可以通过代码以您希望的方式调用 API 方法并对其进行操作。这些是自动完成小部件上可用的 API 方法。
关闭
close()方法关闭自动完成小部件上的下拉列表。
// get a reference to the autocomplete widget
var autocomplete = $("autocomplete").data("kendoAutoComplete");
autocomplete.close();
数据项
dataItem()方法返回指定索引处的数据记录。此dataItem对象将是自动完成小部件数据源在指定索引处的特定对象。在我们的示例中,它将是一个特定的stateOrTerritory对象。
var autocomplete = $("#autocomplete").data("kendoAutoComplete");
// get the dataItem corresponding to the passed index.
var dataItem = autocomplete.dataItem(1);
销毁
destroy()方法准备自动完成小部件以安全地从 DOM 中移除。它断开所有事件处理程序并删除数据属性。它实际上并不执行从 DOM 中移除的最后一步;这是您必须自己编写的操作。
var autocomplete = $("#autocomplete").data("kendoAutoComplete");
// detach events
autocomplete.destroy();
启用
enable()方法切换自动完成小部件的开启和关闭状态。
// get a reference to the autocomplete widget
var autocomplete = $("autocomplete").data("kendoAutoComplete");
// disables the autocomplete
autocomplete.enable(false);
// enables the autocomplete
autocomplete.enable(true);
刷新
refresh()方法重新渲染自动完成小部件下拉列表中的项。
// get a reference to the Kendo UI AutoComplete
var autocomplete = $("autocomplete").data("kendoAutoComplete");
// re-render the items in drop-down list.
autocomplete.refresh();
搜索
search()方法使用提供的参数过滤自动完成小部件的数据源,然后重新绑定下拉列表。
// get a reference to the autocomplete widget
var autocomplete = $("autocomplete").data("kendoAutoComplete");
// Searches for item which has "Inception" in the name.
autocomplete.search("Inception");
选择
select()方法从自动完成小部件中选择下拉列表项,并设置自动完成输入框的文本。
// get a reference to the autocomplete widget
var autocomplete = $("autocomplete").data("kendoAutoComplete");
// selects by jQuery object
autocomplete.select(autocomplete.ul.children().eq(0));
建议
suggest() 方法将建议强制应用到自动完成小部件的文本上。
// note that this suggest is not the same as the configuration method
// suggest which enables/disables auto suggesting for the AutoComplete
//
// get a reference to the Kendo UI AutoComplete
varautoComplete = $("#autoComplete").data("kendoAutoComplete");
// force a suggestion to the item with the name "Inception"
autoComplete.suggest("Inception");
价值
value() 方法获取或设置自动完成小部件的值。
// get a reference to the autocomplete widget
var autocomplete = $("autocomplete").data("kendoAutoComplete");
// get the text of the autocomplete.
var value = autocomplete.value();
摘要
自动完成小部件是帮助网站用户的一个很好的方式。任何输入框的值可以被预测的情况,例如当它们来自一组特定的值或搜索常用术语时,自动完成小部件将立即使您的网站更容易使用,并且用户肯定会注意到并欣赏它。不仅如此,配置非常简单,您几乎不需要费力就可以启用它。
在下一章中,我们将学习如何使用日历小部件。这个小部件将允许您在网页中显示和配置交互式日历控件,以便用户可以轻松选择日期。它还将为您提供一种将数据绑定到日历上的方法,以显示重要日期。Telerik Kendo UI 日历小部件将帮助将复杂的 JavaScript 日历转换为开发出色网页的简单易用工具。
第三章. 使用和自定义日历
日历长期以来一直是需要一些巧妙 JavaScript 的网页功能。HTML5 正在努力使这一切变得更加简单,但浏览器的支持仍然不统一。这就是 Kendo UI 成为完美解决方案的地方,因为它是一个结合 HTML5 和 JavaScript 的框架,使用最新的标准创建跨浏览器的统一性。像往常一样,Kendo UI 解决方案的实施非常简单。
日历小部件 – 基础
Kendo UI 日历小部件将一个简单的 HTML 元素,例如div,转换成一个显示日历的专用 HTML 表格。它还为此表格连接 JavaScript 功能,以支持所有日历小部件事件和方法。要查看此小部件的最简单实现,请在 Kendo 控制器中创建一个新的操作方法,以便我们有一个“calendar”的 URL:
public ActionResult Calendar()
{
return View();
}
然后为这个操作方法添加一个视图,并设置一个空的div来容纳 Kendo 日历小部件:
@{
ViewBag.Title = "Calendar";
}
Calendar
$("#calendar").kendoCalendar();
考虑到我们编写的代码如此之少,输出效果令人惊叹:
在日历上点击并观察它已经具有多少功能。日历顶部的箭头可以向前或向后导航一个月。日历顶部的文本(如前一个截图中的2012 年 10 月所示)可以导航到更广泛的日期级别,这使得选择不同的年份或十年变得容易。日历底部的日期是一个超链接,可以直接导航到当前日期。随着我们在本章后面添加功能,我们可以使日历做得更多。
配置日历小部件
由于日历小部件只有少数属性,让我们先检查它们,然后再转到使用它们的示例。日历小部件有两种不同类型的属性:
数据/模板属性:这些属性配置了日历小部件背后的数据
显示/格式化属性:这些属性配置了日历在页面上的渲染方式以及数据的格式化方式
这里以代码格式列出这些属性。将此代码添加到页面并运行它:
$("#calendar").kendoCalendar({
culture: 'en-US', // specifies the culture
depth: 'month', // specifies navigation depth
//(century/decade/month/year)
max: new Date(2012,11,31), // latest date calendar can show
min: new Date(1980, 0, 1), // oldest date calendar can show
start: 'year', // specifies the start view (century/decade/month/year)
value: new Date(2012,9,25), // initially selected date
format: 'yyyy/MM/dd' // format string used when the value() of this
//calendar is requested
});
当日历配置如上所述时,它最初通过显示当前选中年份内可用的月份来渲染(start: 'year')。由于我们已配置它允许进入每个月份的导航深度(depth: 'month'),我们可以点击一个月份,然后看到该月份及其所有可用日期:
注意
即使当前视图是“年”,今天的日期仍然在日历页脚中可见。
谈到页脚,让我们看看日历提供的哪些数据/模板属性。这里的主要属性有三个:data、month和footer,它们是自定义日历小部件的主要方式。为了演示如何自定义日历中特定日期的简单示例,将以下代码添加到页面中并运行它:
Calendar
.specialDay {
color: white;
background-color: orange;
border:1px solid black;
}
# if ($.inArray(+data.date, data.dates) != -1) { #
# } else { #
#= data.value #
# } #
var datesArray = [+new Date(2012, 5, 15), +new Date(2012, 5, 21)];
$("#calendar").kendoCalendar({
culture: 'en-US', // specifies the culture
depth: 'month', // specifies navigation depth
max: new Date(2012, 5,31), // latest date calendar can show
min: new Date(2012, 5, 1), // oldest date calendar can show
start: 'month', // specifies the start view
value: new Date(2012,5,11), // initially selected date
format: 'yyyy/MM/dd', // format string used to format the date values
dates: datesArray,
month: {
content: $("#redDays").html(),
empty: "X"
},
footer: "Today is #= kendo.toString(data, 'd') #"
});
让我们一起逐步分析这段代码。首先,我们有一个特殊的样式指令,用于显示特殊日期的方式。在这种情况下,橙色背景上的白色文本和实线黑色边框。我们还指定了一个 Kendo UI 模板块和 JavaScript,以确定正在渲染的日期是否是我们特殊日期之一。如果是特殊日期之一,则我们希望将其应用自定义样式;否则,按常规渲染。
.specialDay {
color: white;
background-color: orange;
border:1px solid black;
}
# if ($.inArray(+data.date, data.dates) != -1) { #
# } else { #
#= data.value #
# } #
接下来,我们定义日历小部件的实际配置。在这里,month属性和dates属性之间的关系变得明显:dates属性提供了month属性用来在日历上渲染日期所需的数据。在我们定义的模板中,我们检查当前正在渲染的日期是否包含在dates数组中,然后使用data.value来渲染当前正在执行的日期编号。注意,我们还将在dateArray中的日期前面加上加号+,以强制它们成为我们可以轻松与$.inArray()比较的数字日期。这不是每个情况下的要求,但适用于这个示例。
var datesArray = [+new Date(2012, 5, 15), +new Date(2012, 5, 21)];
$("#calendar").kendoCalendar({
culture: 'en-US', // specifies the culture
depth: 'month', // specifies navigation depth
max: new Date(2012, 5,31), // latest date calendar can show
min: new Date(2012, 5, 1), // oldest date calendar can show
start: 'month', // specifies the start view
value: new Date(2012,5,11), // initially selected date
format: 'yyyy/MM/dd', // format string used to format the date values
dates: datesArray,
month: {
content: $("#redDays").html(),
empty: "X"
},
footer: "Today is #= kendo.toString(data, 'd') #"
});
其他需要注意的事项是,有一个名为footer的新属性,用于渲染日历页脚的模板,它可以通过传递给它的data属性访问今天的日期。此外,请注意,month对象还有一个名为empty的属性,用于渲染位于min或max属性值范围之外的日期。
以这种方式设置日历后,在浏览器中的外观如下:
注意
注意通过dateArray提供的日期的特殊显示、范围之外的日期以及页脚中使用的新的文本。
使用 MVC 的日历小部件
日历小部件也可以通过 ASP.NET MVC 扩展方法进行配置。为了模仿我们刚刚创建的日历,你可以用以下代码替换你的视图内容:
Calendar
.specialDay {
color: white;
background-color: orange;
border:1px solid black;
}
# if ($.inArray(+data.date, data.dates) != -1) { #
# } else { #
#= data.value #
# } #
var datesArray = [+new Date(2012, 5, 15), +new Date(2012, 5, 21)];
@(Html.Kendo().Calendar()
.Name("mvcCalendar")
.Depth(CalendarView.Month)
.Max(new DateTime(2012, 6, 30))
.Min(new DateTime(2012, 6, 1))
.Start(CalendarView.Month)
.Value(new DateTime(2012, 6, 11))
.Format("yyyy/MM/dd")
.MonthTemplate("# if ($.inArray(+data.date, datesArray) != -1) { #" +
"
"# } else { #" +
"#= data.value #" +
"# } #")
.Footer("Today is #= kendo.toString(data, 'd') #")
)
使用此新代码的输出如下:
很相似,不是吗?注意,MVC 扩展隐藏了max和min以下的日期,并且在month上没有提供空属性。还有一些其他独特的事情需要注意。首先,请注意我们仍然在视图中通过 JavaScript 使用日期数组。这是因为month模板是在 JavaScript 中运行的,而不是通过 MVC 扩展,并且需要在客户端访问这些数据。由于这个原因,以及 MVC 扩展不提供dates属性的事实,我们必须将模板从使用data.dates更改为实际的 JavaScript 数组名称——datesArray。在这个例子中,我直接将模板代码输入到 MVC 扩展方法中,但还有一个名为MonthTemplateId()的方法,你可以传递页面上已经存在的模板的 HTML id。
此外,请记住始终在每个 Kendo MVC 扩展对象上调用.Name()方法;这是代码正常工作的必要条件。这就是 MVC 扩展方法如何为渲染的 HTML 输出分配唯一的name和id属性,以及如何将所有 JavaScript 方法和事件正确地连接到网页浏览器中。如果你不包括.Name()方法,当你尝试运行页面时,你也会看到运行时错误。
日历小部件上的可用方法
日历小部件公开了几个可以在页面上与之交互的方法。这些方法可以通过更改其属性或实时触发特定功能来配置小部件。以下是针对 Kendo UI 日历小部件的特定可用方法的代码形式:
var calendar = $("calendarId").data("kendoCalendar");
// Set a new max date
calendar.max(new Date(2013,11,31));
// Retrieve the current max date
var lastDay = calendar.max();
// Set a new min date
calendar.min(new Date(2011, 11, 31);
// Retrieve the current min date
var oldestDay = calendar.min();
// Navigate to a specific date using a specific view
calendar.navigate(new Date(2012,2,5), "month");
// Navigate down to a lower view (i.e. goes from "year" to "month")
calendar.navigateDown(new Date(2012,6,7)); // date is optional
// Navigate to the future
calendar.navigateToFuture();
// Navigate to the past
calendar.navigateToPast();
// Navigate up to a higher view (i.e. goes from "year" to "decade")
calendar.navigateUp("year");
// Set a new value (selected date) for the calendar
calendar.value(new Date(2012,4,7));
// Get the current value (selected date) of the calendar
var selectedDate = calendar.value();
让我们以其中的一些为例,看看它们在我们页面上的实际效果。修改我们为 MVC 视图创建的代码如下:
Calendar
.specialDay {
color: white;
background-color: orange;
border:1px solid black;
}
# if ($.inArray(+data.date, data.dates) != -1) { #
# } else { #
#= data.value #
# } #
var datesArray = [+new Date(2012, 5, 15), +new Date(2012, 5, 21)];
@(Html.Kendo().Calendar()
.Name("mvcCalendar")
.Depth(CalendarView.Month).Start(CalendarView.Month)
.Value(new DateTime(2012, 6, 11))
.Format("yyyy/MM/dd")
.MonthTemplate("# if ($.inArray(+data.date, datesArray) != -1) { #" +
"
"# } else { #" +
"#= data.value #" +
"# } #")
.Footer("Today is #= kendo.toString(data, 'd') #")
)
$("#navigateUp").click(function () {
var calendar = $("#mvcCalendar").data("kendoCalendar");
calendar.navigateUp();
});
$("#navigateDown").click(function () {
var calendar = $("#mvcCalendar").data("kendoCalendar");
calendar.navigateDown(calendar.value());
});
$("#showValue").click(function () {
var calendar = $("#mvcCalendar").data("kendoCalendar");
alert(calendar.value());
});
为了将日历小部件作为 JavaScript 对象使用,我们必须在包含我们创建的日历的页面元素上调用.data()函数。点击页面上的按钮并观察它们的行为。这应该能给你一些关于日历小部件能提供什么以及如何将你自己的交互式代码插入日历以改善用户体验的思路。
日历小部件触发的事件
Kendo UI 日历小部件有两个事件——change和navigate。这些事件在它们命名的动作发生后触发。当选定的日期改变时,Change事件被触发,当日历被导航时(例如,当月份改变或视图从“月份”移动到“年份”时),navigate事件被触发。
如果你想让日历只在用户在页面上选择某个输入框时出现,并将它的值放入该输入元素中,你会怎么做?你可以尝试这样做。修改我们正在工作的页面的最终script块,使其看起来像以下示例:
$(function () {
$("#mvcCalendar").hide();
});
$(document).ready(function () {
$("#mvcCalendar").data("kendoCalendar").bind("change", function (e) {
var date = $("#mvcCalendar").data("kendoCalendar").value();
$("#showTheCalendar").val(kendo.toString(date, 'd'));
});
});
$("#showTheCalendar").focusin(function () {
$("#mvcCalendar").slideDown();
});
$("#nameInput").focusin(function () {
$("#mvcCalendar").slideUp();
});
$("#ageInput").focusin(function () {
$("#mvcCalendar").slideUp();
});
$("#navigateUp").click(function () {
var calendar = $("#mvcCalendar").data("kendoCalendar");
calendar.navigateUp();
});
$("#navigateDown").click(function () {
var calendar = $("#mvcCalendar").data("kendoCalendar");
calendar.navigateDown(calendar.value());
});
$("#showValue").click(function () {
var calendar = $("#mvcCalendar").data("kendoCalendar");
alert(calendar.value());
});
在这里,我们有一些由简单的 jQuery 和 jQuery UI 连接的事件,它们可以显示或隐藏日历,并在选择时获取其值。日历的 change 事件用于确定何时将新的日期值放入页面的输入元素中。这就是页面首次渲染时的样子。
日历在用户点击第一个文本框之前是隐藏的。一旦发生这种情况,我们连接的事件就会使日历出现,以便用户可以选择页面的适当日期。
摘要
Kendo UI 日历小部件易于配置,并在您的页面上提供了一个丰富的元素,可以使处理日期变得简单得多。它可以由 JavaScript 或 MVC 扩展进行配置,并使用 Kendo 模板进行高度可定制的格式化和显示。我仅展示了使用模板和事件可以完成的基本示例;您可以使用这些示例来创建一些非常有用的交互式内容。
在下一章中,我们将学习 Kendo UI 框架中最强大的功能之一,即模型-视图-视图模型(MVVM)框架。这个框架允许您通过简单的 HTML 属性将数据和功能绑定到您的页面上,并启用对数据的实时更改,或对服务器进行更改,同时为用户提供即时反馈。MVVM 框架是一个您希望在所有页面上都想要使用的强大工具。
第四章:Kendo MVVM 框架
自从 JavaScript 的诞生以及丰富 MVVM 框架的出现以来,JavaScript 开发已经走了很长的路,这无疑是这一演变的美好证明。这些框架允许开发者将代码中的责任分离,以更好地处理复杂性。它们还提供了一个简洁的语法,使得 MVVM 框架本身可以处理将动态数据绑定到网页上的繁琐工作。如果你之前从未使用过 JavaScript MVVM 框架,那么 Kendo MVVM 框架将为你带来一场盛宴。
理解 MVVM - 基础
MVVM 代表模型(M)、视图(V)和视图模型(VM)。它是与系统架构相关的设计模式家族的一部分,将责任分离到不同的单元中。一些其他相关的模式是模型-视图-控制器(MVC)和模型-视图-表示器(MVP)。它们在框架的每个部分负责的内容上有所不同,但它们都试图通过相同的设计原则来管理复杂性。在这里不深入不必要的细节,只需说这些模式对于开发可靠和可重用的代码是好的,如果你正确实现了它们,你无疑会从中受益。幸运的是,好的 JavaScript MVVM 框架通过为你连接组件,让你专注于代码而不是“管道”工作,使得这变得容易。
在 Kendo UI 的 JavaScript MVVM 模式中,你需要为想要显示和操作的数据(模型)、结构化整个网页的 HTML 标记(视图)以及处理用户输入、响应事件并将静态标记转换为动态元素的 JavaScript 代码(视图模型)创建定义。另一种说法是,你将拥有数据(模型)、展示(视图)和逻辑(视图模型)。
在实践中,模型是 MVVM 模式中最不明确的部分,甚至不一定在实现中作为一个独特的实体存在。视图模型可以直接在其内部包含模型数据属性,而不是作为单独的单元引用它们,从而承担模型和视图模型的双重角色。这是可以接受的,并且在 ASP.NET MVC 中也可以看到,当视图使用ViewBag或ViewData集合而不是引用强类型模型类时。如果模型没有像视图模型和视图那样定义得很好,请不要让它困扰你。任何模式的实现都应过滤到对您的应用程序真正有意义的内容。
简单的数据绑定
作为入门示例,考虑您有一个需要显示数据表的网页,并且还提供用户通过单击特定的单行或元素与该数据交互的能力。数据是动态的,因此您事先不知道将显示多少条记录。此外,任何更改都应该立即反映在页面上,而不是等待从服务器完全刷新整个页面。您如何实现这一点?
一种传统的方法是使用可以动态从数据源创建表格并能够连接一些 JavaScript 交互性的特殊服务器端控件。这种方法的问题通常是需要服务器和浏览器之间进行一些复杂的额外通信,无论是通过“视图状态”,隐藏字段,还是长而丑陋的查询字符串。此外,这些特殊控件生成的输出通常很难进行定制或以重要方式进行操作,这减少了您网站外观和行为的选择。另一个选择是创建特殊的 JavaScript 函数,以异步从端点检索数据,在表格内生成 HTML 标记,然后为按钮和链接连接事件。这是一个好的解决方案,但需要大量的编码和复杂性,这意味着调试和精炼可能需要更长的时间。这也可能超出了某些开发者没有重大研究的能力范围。第三个选项,通过 JavaScript MVVM 如 Kendo UI 提供,在这两种位置之间取得平衡,通过减少 JavaScript 的复杂性,但仍然在页面内提供强大且简单的数据绑定功能。
创建视图
这是一个简单的 HTML 页面,展示视图是如何基本工作的:
th {
width: 135px;
}
| Name | Hair Color | Favorite Food |
|---|
这里有一个简单的table元素,包含三个列,但不是body包含任何tr元素,而是有一些特殊的 HTML5 data-*属性,表明这里正在进行一些特殊操作。这些data-*属性本身并不做任何事情,但 Kendo UI 会读取它们(如下所示),并解释它们的值,以便将视图与视图模型链接起来。data-bind属性指示 Kendo UI,这个元素应该绑定到一个名为people的对象集合。
data-template属性告诉 Kendo UI,应该使用 Kendo UI 模板格式化people对象。以下是模板的代码:
这是一个简单的模板,为表格中的每一行定义了一个tr结构。td元素上也有data-bind属性,这样 Kendo UI 就知道要插入某个属性的值作为 HTML 元素的“文本”,在这种情况下意味着将值放置在
创建模型和视图模型
为了建立这个连接,我们需要一个执行数据绑定的视图模型。以下是这个视图的视图模型代码:
var viewModel = kendo.observable({
people: [
{name: "John", hairColor: "Blonde", favoriteFood: "Burger"},
{name: "Bryan", hairColor: "Brown", favoriteFood: "Steak"},
{name: "Jennifer", hairColor: "Brown", favoriteFood: "Salad"}
]
});
kendo.bind($("body"), viewModel);
通过调用 kendo.observable() 来声明一个 Kendo UI 视图模型,这会创建一个 可观察对象,该对象用于视图中的数据绑定。可观察对象是一个特殊对象,它将一个普通的 JavaScript 变量包装起来,并在该变量的值发生变化时触发事件。这些事件会通知 MVVM 框架更新任何使用该变量值的绑定数据,以便它们可以立即更新并反映变化。这些数据绑定也是双向的,因此如果绑定到可观察对象变量的字段发生变化,绑定到该字段的变量也会实时更改。
在这个例子中,我创建了一个名为 people 的数组,其中包含三个具有关于一些人的属性的对象。这个数组在这个例子中充当模型,因为它包含了数据和数据的结构定义。在代码示例的末尾,你可以看到调用 kendo.bind($("body"), viewModel),这是 Kendo UI 实际执行 MVVM 连接的方式。我传递了一个 jQuery 选择器作为 body 标签的第一个参数,因为这个 viewModel 对象适用于我的整个 HTML 页面,而不仅仅是其中的一部分。
将所有内容组合起来,以下是这个简化示例的完整源代码:
th {
width: 135px;
}
| Name | Hair Color | Favorite Food |
|---|
var viewModel = kendo.observable({
people: [
{name: "John", hairColor: "Blonde", favoriteFood: "Burger"},
{name: "Bryan", hairColor: "Brown", favoriteFood: "Steak"},
{ name: "Jennifer", hairColor: "Brown", favoriteFood: "Salad" }
]
});
kendo.bind($("body"), viewModel);
这里是页面动作的截图。注意 JavaScript people 数组中的数据是如何自动填充到表格中的:
尽管这个例子包含模型、视图和视图模型,但所有三个单元都出现在同一个 HTML 文件中。当然,您可以将 JavaScript 分离到其他文件中,但将它们像这样放在一起也是可以接受的。希望您已经看到了这个 MVVM 框架能为您做什么。
可观察数据绑定
使用声明性属性将数据绑定到您的 HTML 网页(视图)中是非常好且非常有用的,但 MVVM 框架还提供了一些更重要的功能,我们在上一个示例中没有看到。MVVM 框架不仅将数据附加到视图并保持不变,而且还维护了视图模型所有属性的运行副本,并实时更新这些属性的引用。这就是为什么视图模型是用名为 "observable" 的函数创建的原因。内部属性是可观察的,它们会向上报告变化,以便数据绑定的字段始终反映最新的数据。让我们看看一些示例。
动态添加数据
在我们刚刚看到的示例的基础上,在 HTML 页面中的表格下方添加这条水平线和表单:
这在页面上添加了一个表单,以便用户可以输入应出现在表格中的新人员数据。注意,我们添加了一些data-bind属性,但这次我们绑定的是输入字段的value而不是text。还要注意,我们在表单底部的button上添加了一个data-bind属性,将那个button的click事件与视图模型内部的函数绑定。通过将click事件绑定到addPersonJavaScript 方法,每次点击这个按钮时,addPerson方法都会被触发。
这些绑定始终将那些输入字段的值与视图模型对象链接起来。如果其中一个输入字段的值发生变化,例如当用户在框中输入某些内容时,视图模型对象会立即看到这个变化,并更新其属性以匹配;它还会更新任何绑定到该属性值的页面区域,以便它们与新的数据匹配。
按钮的绑定是特殊的,因为它允许视图模型对象将自身的事件处理器附加到该元素的点击事件上。将事件处理器绑定到事件本身并不是什么特别的事情,但通过这种方式(通过data-bind属性)进行绑定是很重要的,这样页面内的特定运行视图模型实例就能将其中的一个函数附加到该事件上,使得事件处理器内部的代码能够访问这个特定视图模型的数据属性和值。这还允许将一个非常具体的上下文传递给事件,否则将很难访问。
下面是我在视图模型中添加到people数组下面的代码。在这个例子中,我们拥有的前三个属性构成了模型。它们包含被观察并绑定到页面其余部分的数据:
personName: "", // Model property
personHairColor: "", // Model property
personFavFood: "", // Model property
addPerson: function () {
this.get("people").push({
name: this.get("personName"),
hairColor: this.get("personHairColor"),
favoriteFood: this.get("personFavFood")
});
this.set("personName", "");
this.set("personHairColor", "");
this.set("personFavFood", "");
}
你看到的第一个几个属性是我们上面在输入表单中绑定的相同属性。它们以空值开始,因为当页面首次加载时,表单不应该有任何值。在视图模型内部声明这些空属性仍然很重要,以便在它们发生变化时跟踪它们的值。
数据属性之后的函数addPerson是我们绑定到输入表单按钮点击事件的函数。在这个函数中,我们正在访问people数组,并根据用户在表单字段中提供的信息向其中添加一条新记录。请注意,我们必须使用this.get()和this.set()函数来访问视图模型内部的数据。这很重要,因为在这个视图模型中的属性是特殊的可观察属性,直接访问它们的值可能不会得到你预期的结果。
您应该注意到的最重要的事情是addPerson函数与页面上的数据通过 View-Model 属性进行交互。它没有使用 jQuery、document.querySelector或任何其他 DOM 交互来读取元素的值!由于我们在输入元素的值上声明了data-bind属性到我们的 View-Model 属性,我们可以通过访问 View-Model 本身始终从这些元素中获取值。这些值始终被跟踪。这允许我们在addPerson函数和 HTML 页面中检索并更改这些 View-Model 属性,HTML 页面将立即显示这些更改。通过在属性上调用this.set()并将它们的值更改为空字符串,HTML 页面将清除用户刚刚输入并添加到表格中的值。再一次,我们更改 View-Model 属性而不需要我们自己访问 HTML。
这里是这个示例的完整源代码:
th {
width: 135px;
}
| Name | Hair Color | Favorite Food |
|---|
var viewModel = kendo.observable({
people: [
{name: "John", hairColor: "Blonde", favoriteFood: "Burger"},
{name: "Bryan", hairColor: "Brown", favoriteFood: "Steak"},
{name: "Jennifer", hairColor: "Brown", favoriteFood: "Salad"}
],
personName: "",
personHairColor: "",
personFavFood: "",
addPerson: function () {
this.get("people").push({
name: this.get("personName"),
hairColor: this.get("personHairColor"),
favoriteFood: this.get("personFavFood")
});
this.set("personName", "");
this.set("personHairColor", "");
this.set("personFavFood", "");
}
});
kendo.bind($("body"), viewModel);
这里是页面动作的截图。您会看到通过填写表格,表中已经增加了一名额外的人员。自己试一试,看看这个代码与您之间的即时交互:
在 View 中使用可观察属性
我们刚刚看到在 View-Model 中向可观察集合添加新数据是多么简单,以及这如何导致任何数据绑定元素立即显示新数据。让我们添加一些更多功能来展示如何处理单个元素,并看看它们的可观察值如何更新页面内容。
为了演示这个新功能,我在表格中添加了一些列:
| Name | Hair Color | Favorite Food | Live Data |
|---|
第一列新列没有标题文本,但将在页面上为每个表格行包含一个按钮。第二列新列将显示表格中显示的每个对象的“实时数据”在 View-Model 中的值。
这里是更新后的行模板:
-
注意,我已经将所有的简单text data-bind属性替换为输入元素和valuedata-bind属性。我还添加了一个带有clickdata-bind属性的按钮和一个显示三个属性文本的列,这样您就可以实时看到可观察行为。
View-Model 为删除按钮获得了一个新方法:
deletePerson: function (e) {
var person = e.data;
var people = this.get("people");
var index = people.indexOf(person);
people.splice(index, 1);
}
当通过 Kendo UI 创建的绑定调用此函数时,它会传递一个事件参数,这里称为e,到包含数据属性的函数中。这个数据属性是对用于渲染特定数据行的模型对象的引用。在这个函数中,我创建了一个person变量来引用这一行的人员,以及一个对people数组的引用;然后我们使用这个人员的索引从数组中移除它。当您点击删除按钮时,您可以观察到表格立即对变化做出反应。
这里是更新后的 View-Model 的完整源代码:
-
var viewModel = kendo.observable({
people: [
{name: "John", hairColor: "Blonde", favoriteFood: "Burger"},
{name: "Bryan", hairColor: "Brown", favoriteFood: "Steak"},
{name: "Jennifer", hairColor: "Brown", favoriteFood: "Salad"}
],
personName: "",
personHairColor: "",
personFavFood: "",
addPerson: function () {
this.get("people").push({
name: this.get("personName"),
hairColor: this.get("personHairColor"),
favoriteFood: this.get("personFavFood")
});
this.set("personName", "");
this.set("personHairColor", "");
this.set("personFavFood", "");
},
deletePerson: function (e) {
var person = e.data;
var people = this.get("people");
var index = people.indexOf(person);
people.splice(index, 1);
}
});
kendo.bind($("body"), viewModel);