AngularJS 中的数据
在本文中,我们将向您展示如何使用包括 Angular 和 Breeze 在内的免费开源库来构建可以查询、保存和维护客户端实体模型数据的 JavaScript 应用程序。
引言
构建一个在桌面、平板电脑和其他移动设备上都能同样运行的数据丰富型企业应用程序,说起来容易做起来难。在相当长的一段时间里,使用 HTML、CSS 和 JavaScript 构建的单页应用程序 (SPA) 几乎不可能与原生应用程序相媲美。但时代正在改变……而且变化迅速。
在本文中,我们将向您展示如何使用包括 Angular 和 Breeze 在内的免费开源库来构建可以查询、保存和维护客户端实体模型数据的 JavaScript 应用程序。
挑战
在 Windows 窗体、WCF 或 Silverlight (.NET) 或 Swing、SWT 或 AWT (Java) 等桌面/富客户端应用程序中,您可以将屏幕控件直接绑定到丰富的模型数据,从而使 UI 更新能够传播到实体,反之亦然。 .NET 和 Java 都拥有丰富的基于 ORM 的实体管理器,例如 Entity Framework 和 Hibernate,它们允许查询实体、跟踪更改并执行工作单元保存。
我们如何为 JavaScript 应用程序使用类似的编程模型?
解决方案
Angular 和 Breeze 使之成为可能。
Angular 专注于演示。它负责 UI 和数据模型之间的绑定。HTML 元素中的更改会传播到 JavaScript 实体,反之亦然。
Breeze 专注于数据管理。Breeze 查询、保存并处理客户端和服务器之间所有的数据交互。Breeze 会自动创建 JavaScript 模型对象(称为“实体”),这些对象与来自远程服务的数据的形状相匹配。它添加了业务规则以及支持验证、更改跟踪和导航到相关实体的基础设施。Breeze 导航属性可以自动遍历关系模型中隐含的对象图,因此您可以从客户遍历到其订单,再从订单遍历到其明细项。Breeze 会跟踪用户的更改,并根据规则进行验证,其中一些规则可能已从服务器传播到客户端。
如果您将数据存储在数据库中,将数据作为复杂对象图进行查询和保存,并在多个视图之间共享对象图——并且希望在 JavaScript 中这样做——那么没有比 Breeze 更好的方法了。
Todo-Angular 示例
我们将使用一个 Todo 应用程序来演示 Angular 和 Breeze 如何使构建单页应用程序比手动编写所有数据绑定和管理更容易。
从 BreezeJS.com 下载示例包。Todo-Angular 示例(包括所有源代码和必需的库)可以在 /Samples/Todo-Angular/ 目录中找到。在 Visual Studio 中打开它,生成它,然后运行它——调试(F5)或不调试(Ctrl-F5)。
Todo-Angular 是一个简单的 CRUD(创建、读取、更新、删除)应用程序。在一个屏幕上,您可以创建项目、更新它们的描述、更改它们的状态或删除它们。所有应用程序逻辑都在客户端的 JavaScript 中执行。服务器仅通过查询和保存 Todo 到服务器端的 SQL 数据库来响应数据请求。
基本工作流程如下:
- Breeze 向服务器发送查询以检索 Todo 项目。服务器发送 JSON 响应,Breeze 将其在客户端转换为 Todo 实体(丰富的 JavaScript 对象)。
- Angular 将 Todo 实体绑定到 HTML 元素(例如,文本框、复选框),并监听更改。
- 当 HTML 表单上的值发生更改时(例如,用户编辑 Todo 的描述),Angular 会检测到更改并将新值传播到底层的 Todo 实体。
- Breeze 检测到 Todo 实体的更改并将其标记为已修改。
- 触发保存时,Breeze 将已更改的 Todo 实体发送到服务器。
在幕后,应用程序架构是模型-视图-控制器 (MVC),Angular 和 Breeze 都很好地遵循这一点。
只有一个网页,Index.html,其主要职责是托管客户端应用程序。因此,它在顶部有 CSS,在底部有脚本,在中间有一个布局部分。
View
为了简单起见,Todo 视图——显示 Todo 并接受用户输入的 HTML——也嵌入在 HTML 中,位于折叠的 "applicationHost
" <div>
内。
更复杂的应用程序(带有多个客户端页面)将在单独的文件中定义 Todo 视图(和其他视图),客户端应用程序逻辑会将这些文件加载到 "applicationhost
" 中并从中移除。这是未来的发展方向。在这个示例中,我们只是想掌握基础知识。
以下是视图 HTML 的几个摘录,叠加在其定义的视觉效果上。
Angular 数据绑定
"data-ng- ..." 属性是 Angular 的“指令”。指令将 HTML 小部件的各个方面绑定到控制器中的属性和方法。
许多指令都绑定到控制器本身的成员。以下是 controller.js 中定义的控制器摘录。
对于 Angular 新手来说,$scope
是数据绑定对象。您可以将其视为 ViewModel,如果这个术语对您来说更熟悉的话。
当 Angular 创建控制器的新实例时,它会将一个空的 $scope 对象传递给控制器的定义函数;这就是上面图片中的匿名函数。
控制器将属性和方法添加到此 $scope 中,命名方式与 HTML 中的声明相匹配。
在下面的快照中,我们看到 HTML 中的 "addItem()
" 与 $scope.addItem
方法匹配,而与输入文本框关联的 "newTodo
" 与 $scope.newTodo
匹配。
$scope.items
数组保存了通过查询检索到的 Todo 项目。它被绑定到一个“repeater”,即 <li>
元素,该元素描述了显示单个 Todo 项目的模板。
该模板中的一些 Angular 指令绑定到单个 Todo 项目实体而不是控制器的属性。例如,复选框绑定到项目的 IsDone
属性,而标签绑定到项目的 Description
属性。
自定义指令
Angular 的优势之一是能够通过自定义指令扩展其本机绑定指令,以满足应用程序特定需求。此模块定义了一些自定义 Angular 指令,它们可以监听焦点更改。
在这里,我们看到在 controller.js 文件中定义的自定义 "onBlur
" 指令已应用于项目描述文本框("data-on-blur
")。
当用户在编辑完描述后离开文本框时,文本框会失去焦点。指令检测到“blur”并调用控制器上的 "completeEdit
" 方法。
从更广泛的角度来看,我们看到控制器通过在视图和模型层之间进行协调来管理应用程序工作流。它依赖 Angular 来获取屏幕信息,并在需要获取和操作 Todo 项目时委托给 dataservice
。
dataservice
处理所有实体创建、查询和保存。这些方法通常会为异步操作返回承诺,以便控制器可以在这些操作完成后稍后做出相应的响应。
Breeze 本身会将控制器命令转换为到服务器上单个 ASP.NET Web API 控制器的 HTTP 请求。该控制器将委托给 Breeze.NET 组件 EFContextProvider
,该组件处理与 Entity Framework 和应用程序的“Code First”TodoItem 模型之间的交互。原始数据存储在 SQL Server CE 数据库中。
一点点 Breeze
Breeze 的文档、实时教程和示例是了解 Breeze 开发的最佳方式。让我们看一个 dataservice.js 方法,以激发您的兴趣。
控制器传入一个标志,告诉数据服务是返回所有 Todo(包括已存档的 Todo)还是仅返回活动的 Todo。该标志来自控制器中的 isArchived
属性,该属性绑定到“显示已存档”复选框。
我们首先创建一个 Breeze 查询对象,该对象以名为“Todos”的远程服务方法为目标。如果我们现在执行该查询,远程服务方法将返回数据库中的每个 Todo 项目。
但是,开发人员编写了该服务方法,因此它将响应 OData 样式的查询。因此,在客户端,我们添加了一个 orderBy
子句,指示服务按创建日期对查询结果进行排序。排序将在数据层进行,在数据传输过来之前。
如果用户只想查看活动的 Todo(即,includeArchived
标志为 false),我们需要排除已存档的 Todo,添加一个过滤器。 "where" 子句将此过滤器添加到查询中。现在,当我们执行查询时,结果将仅包含活动的 Todo,按创建日期排序。
这种查询构建策略和语法应该会让您想起 LINQ。
在最后一个语句中,Breeze EntityManager
执行查询。查询操作是异步的,并向控制器返回一个承诺:承诺在查询成功或失败时通知控制器。以下是控制器调用数据服务并处理承诺的方式。
稍作停顿后,远程服务将 Todo 项目数据作为 JSON 返回到客户端。Breeze 将该 JSON 转换为 Breeze 实体,这些实体具备 Breeze 系统的验证、更改跟踪和其他功能。这些实体在返回给控制器作为查询结果之前,会合并到 EntityManager
的缓存中。
当用户在屏幕上进行更改时,缓存中的相应 Todo 实体会变为“已修改”状态。应用程序设计为立即保存,因此控制器会告诉数据服务,数据服务最终会委托调用 EntityManager
的 saveChanges
方法。
就是这么简单。管理器会收集所有具有未保存更改的缓存实体,并将它们作为更改集发送到远程服务。远程服务会解压缩更改集,应用适当的业务逻辑,并将其作为单个事务存储到数据库中。业务逻辑(在此示例中很少)由您决定;其余的管道由 Breeze 处理。
我们应该提到,此示例中的 dataservice.js 与使用 Knockout 而不是 Angular 的并行 Todo 示例的数据服务几乎相同。只需一行配置即可在 Angular、Knockout、Backbone 或您选择的其他模型库之间进行切换。
Breeze 本身与任何特定的 JavaScript 数据绑定库没有内在的亲和性。
这就是 Todo-Angular 应用程序的概要。
JavaScript 库存
Todo-Angular 客户端依赖五个免费的开源第三方库和三个应用程序脚本。
第三方库
所有第三方库都在 Scripts 文件夹中。
Angular.js
Angular 负责处理管道——数据绑定、事件监控和演示层的 DOM 操作。
Breeze.js
Breeze 负责 MVC 三元组中的“模型”相关事宜。它维护一个持久化模型对象的客户端缓存,包括新创建的实体和通过查询物化的实体。Breeze 处理您的查询和保存操作。
jQuery.js
在此特定示例中,jQuery 的使用量不大。Breeze 依赖 jQuery.ajax 与后端服务进行通信。
Q.js
Q 协助通过 CommonJs 承诺管理异步操作。
toastr.js
Toastr 在从右下角弹出的“吐司”窗口中显示过程和错误通知。
客户端脚本
客户端应用程序脚本位于 Scripts/app 文件夹中。
controller.js
controller.js 定义了两个 Angular 模块,TodoMain
和 TodoCtrl
。自定义指令定义在 TodoMain
中。TodoCtrl
是管理视图的控制器模块。
dataservice.js
dataservice.js 为应用程序提供了建模和客户端数据访问层。它在很大程度上依赖于 Breeze。
logger.js
logger.js 仅仅是对第三方 toastr 库的封装。您可以移除 toastr 并选择记录到控制台。
服务器呢?
AngularJS 和 BreezeJS 都是纯 JavaScript 库。它们都不需要 .NET、Visual Studio、Entity Framework 或 ASP.NET,您可以随意编写服务器。
您需要某种服务器来提供 Web 资源和数据服务。我们在此示例中使用了 .NET,因为它对于已经构建在 Microsoft 技术栈上的人来说非常快捷有效。Breeze 附带的组件可以简化 Web API 和 Entity Framework 后端开发,这一点很有帮助。
这个 Todo-Angular 服务器是一个 ASP.NET Web 应用程序。它托管所有客户端资产以及一个 ASP.NET MVC4 Web API 服务,该服务在 Entity Framework Code First 模型的帮助下查询并保存到 SQL Server 数据库。本文侧重于客户端开发,因此目前仅介绍到此。
了解更多
除了核心文档和 API,您还可以找到针对 Todo-Angular 示例的详细信息,包括:
关于
Breeze 由 IdeaBlade 积极开发。请在 Twitter 上关注 @BreezeJS,并在 Facebook 上点赞我们。