使用 ASP.NET MVC 和 Ext JS 构建以数据为中心的 Web 应用程序

原文:

下载代码示例

丰富 Internet 应用程序 (RIA) 将桌面应用程序的可用性与基于 Web 的部署和修订的灵活性结合到了一起。构建 RIA 有两种主要方法。第一种是使用承载执行环境的浏览器插件,如 Flash 插件、Java 插件和 Silverlight 插件。第二种是使用基于 JavaScript 的扩展库,如 Dojo、Ext JS、jQuery、MooTools、Prototype 和 YUI。这两种方法各有利弊。

构建 RIA 通常选择的是 JavaScript 库,因为所有主要浏览器都支持 JavaScript,而无需安装插件或运行时环境。我已经尝试过使用上面提到的另一种库 — Ext JS,而且我发现,对于实现 Web 应用程序,它是一种有趣的选择。它易于实现,文档完善,并且在测试方面与 Selenium 兼容。Ext JS 还提供预定义控件,以简化 Web 应用程序 UI 的创建过程。

遗憾的是,Ext JS 的大部分示例都需要通过服务器端的 PHP、Python 和 Ruby on Rails 代码才能说明。但这并不意味着使用 Microsoft 技术的开发人员不可以利用 Ext JS。虽然将 Ext JS 与 Web 窗体开发集成起来比较困难(这是由于抽象层导致的;为了提供有状态的基于控件的模型,抽象层将封装 Web 的请求-响应特性),但您可以使用 ASP.NET MVC 框架,该框架使您能够在同一应用程序中同时利用 Microsoft .NET Framework 和 Ext JS。

在本文中,我将提供之前没有找到的教程,逐步介绍如何使用可从后端数据库读取并可向后端数据库写入的 ASP.NET MVC 和 Ext JS 来开发实际的 Web 解决方案。

Ext JS 窗体基础知识

要使用 Ext JS,您首先需要从 sencha.com 下载它。(我使用的是 3.2.1 版本,但是您应该获取最新版本。)请注意,免费的开源版 Ext JS 可供开源项目、非营利组织和学习使用。对于其他用途,您需要购买许可证。请参阅 sencha.com/products/license.php 以了解详细信息。

将下载的文件解压缩到您的文件系统的一个目录中。它包含使用 Ext JS 开发 Web 解决方案时所需的所有内容,特别是主文件 ext-all.js。(还有一个调试版本可以帮助您更加轻松地找到错误。)依赖项、文档和示例代码都包含在下载的文件中。

\adapters 和 \resources 是项目必需的文件夹。adapters 文件夹允许将其他库与 Ext JS 一起使用。resources 文件夹包含依赖项,如 CSS 和图像。

要正确使用 Ext JS,您还需要在您的页面中添加三个关键文件引用:


   
     
  1. ext-3.2.1/adapter/ext/ext-base.js
  2. ext-3.2.1/ext-all.js
  3. ext-3.2.1/resources/css/ext-all.css

ext-base.js 文件包含 Ext JS 的核心功能。ext-all.js 包含小组件定义,ext-all.css 包含小组件的样式表。

让我们先通过在静态 HTML 页面中使用 Ext JS 来介绍一下基础知识。页面的开始部分包含以下几行代码,这几行代码链接着成功开发 Ext JS 解决方案所需的文件(我还在 JavaScript 模块中包含了 Ext JS 下载文件中的一些示例小组件):


   
     
  1. <link rel="stylesheet" type="text/css"
  2. href="ext-3.2.1/resources/css/ext-all.css" />
  3. <script type="text/javascript" language="javascript"
  4. src="ext-3.2.1/adapter/ext/ext-base.js"></script>
  5. <script type="text/javascript" language="javascript"
  6. src="ext-3.2.1/ext-all.js"></script>
  7. <script type="text/javascript" language="javascript"
  8. src="extjs-example.js"></script>

我在文件正文中插入了一个 div 元素,以呈现主 Ext JS 窗体:



          <div id="frame"> </div>

        

extjs-example.js 文件对 Ext JS 应用程序的构建方式进行了深入分析。任何 Ext JS 应用程序的模板均使用 Ext.ns、Ext.BLANK_IMAGE_URL 和 Ext.onReady 语句:


   
     
  1. Ext.
  2. ns('formextjs.tutorial');
  3. Ext.BLANK_IMAGE_URL = 'ext-3.2.1/resources/images/default/s.gif';
  4. formextjs.tutorial.FormTutorial = {
  5. ...
  6. }
  7. Ext.onReady(formextjs.tutorial.FormTutorial.init,
  8. formextjs.tutorial.FormTutorial);

Ext.ns 语句使您能够按照逻辑组织命名空间中的代码,以避免命名冲突和作用域问题。

Ext.BLANK_IMAGE_URL 语句对于呈现小组件很重要。它称为空白区域图片(1x1 像素的透明图像),主要用于生成空白区域和放置图标及分隔符。

Ext.onReady 语句是使用 Ext JS 代码进行定义的第一个方法。完全加载 DOM 后会自动调用此方法,以保证脚本运行时您可能引用的所有 HTML 元素都可用。至于 extjs-example.js,下面就是该脚本:


   
     
  1. formextjs.tutorial.FormTutorial = {
  2. init: function () {
  3. this.form = new Ext.FormPanel({
  4. title: 'Getting started form',
  5. renderTo: 'frame',
  6. width: 400,
  7. url: 'remoteurl',
  8. defaults: { xtype: 'textfield' },
  9. bodyStyle: 'padding: 10px',
  10. html: 'This form is empty!'
  11. });
  12. }
  13. }

创建了 Ext.FormPanel 类的实例作为字段的容器。renderTo 属性指向将在其上呈现窗体的 div 元素。defaults 属性指定窗体上组件的默认类型。url 属性指定用于发送窗体请求的 URI。最后,html 属性指定作为默认输出的文本(带有任何 HTML 格式)。

要添加字段,您需要使用 items 属性替换 html 属性:


   
     
  1. items: [ nameTextField, ageNumberField ]

首先要添加的两个项是文本字段和数值字段:



          var nameTextField = new Ext.form.TextField({ fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name' }); var ageNumberField = new Ext.form.NumberField({ fieldLabel: 'Age', value: '25', name: 'age' });

        

所需的属性是:fieldLabel 属性(用于设置窗体组件附带的说明性消息)和 name 属性(用于设置请求参数的名称)。emptyText 属性定义当字段为空时字段将包含的水印文本。value 属性是控件的默认值。

声明控件的另外一种方法是在进行中声明:


   
     
  1. items: [
  2. { fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name' },
  3. { xtype: 'numberfield', fieldLabel: 'Age', value: '25', name: 'age' }
  4. ]

如您所见,对于 Name 字段,您不必指定类型,因为它是从窗体的默认属性获得的。

我将在窗体中添加一些附加元素,结果如图 1 所示。

使用 ASP.NET MVC 和 Ext JS 构建以数据为中心的 Web 应用程序

图 1 完成的窗体

到目前为止,您已经使用 Ext JS 创建了一个窗体来从用户那里获取数据。现在,我们来将这些数据发送到服务器。您需要添加一个按钮,以处理提交过程并向用户显示结果,如图 2 所示。

图 2 窗体按钮


   
     
  1. buttons: [{
  2. text: 'Save',
  3. handler: function () {
  4. form.getForm().submit({
  5. success: function (form, action) {
  6. Ext.Msg.alert('Success', 'ok');
  7. },
  8. failure: function (form, action) {
  9. Ext.Msg.alert('Failure', action.result.error);
  10. }
  11. });
  12. }
  13. },
  14. {
  15. text: 'Reset',
  16. handler: function () {
  17. form.getForm().reset();
  18. }
  19. }]

buttons 属性使窗体能够管理所有可能执行的操作。每个按钮都有 name 和 handler 属性。handler 属性包含与对按钮执行的操作相关的逻辑。在此例中,有两个按钮,分别名为 Save 和 Reset。Save 按钮处理程序执行窗体上的提交操作,并显示一条指示提交成功或失败的消息。Reset 按钮处理程序重置窗体上的字段值。

在窗体创建过程中,最后一个步骤,同时也是很重要的步骤是验证。为了指定必填字段,我们需要将 allowBlank 属性设置为 false,将 blankText 属性设置为所需的验证失败时显示的错误消息。例如,下面是窗体的 Name 字段:


   
     
  1. { fieldLabel: 'Name', emptyText: 'Please, enter a name', name: 'name', allowBlank: false }

如果未在 Name 和 Age 字段中输入任何数据,当您运行应用程序并单击 Save 按钮时,您会收到一条错误消息,并且必填字段下会出现红色下划线。

要自定义针对这些字段的错误消息,在 Ext.onReady 函数下添加下面这行代码:


   
     
  1. Ext.QuickTips.init();

现在,当用户将鼠标指针移动到字段上方时,就会显示一个提示框,其中包含用于说明错误的消息。

我设置了几条字段验证规则,如指定允许的最小和最大长度,将字段验证延迟至提交窗体后进行,为 URL、电子邮件地址和其他类型的数据创建验证函数。您可以在代码下载中查看此验证的详细信息。

构建 Web 应用程序

现在,让我们使用 Ext JS 和 ASP.NET MVC 开发一个 Web 解决方案。我使用的是 ASP.NET MVC 2,但是此解决方案应该也适用于 ASP.NET MVC 3。我接下来要解决的情况是在人力资源管理系统中添加一个员工。

Add Employee 用例说明如下:屏幕提示用户为新员工输入有效信息,如员工 ID、全名、地址、年龄、工资和部门。department 字段是一个用于从中选择部门的部门列表。

如您刚才所见,主要实施策略是在客户端创建一个 Ext JS 窗体,然后使用 ASP.NET MVC 处理数据。持久性层将使用 LINQ 来代表业务实体,并将数据永久保存到数据库系统中。后端数据库是 Microsoft SQL Server 2008。

首先打开 Visual Studio 2010,使用 ASP.NET MVC 2 Web 应用程序模板创建一个新项目。

接下来创建数据库架构。对于此示例,架构将包含两个实体:员工和部门。图 3 显示了我是如何创建 Human Resources 数据库和基础表及约束的。

图 3 创建 Human Resources 数据库


   
     
  1. create table department(
  2. deptno varchar(20) primary key,
  3. deptname varchar(50) not null,
  4. location varchar(50)
  5. );
  6. create unique index undx_department_deptname on department(deptname);
  7. insert into department
  8. values('HQ-200','Headquarter-NY','New York');
  9. insert into department
  10. values('HR-200','Human Resources-NY','New York');
  11. insert into department
  12. values('OP-200','Operations-NY','New York');
  13. insert into department
  14. values('SL-200','Sales-NY','New York');
  15. insert into department
  16. values('HR-300','Human Resources-MD','Maryland');
  17. insert into department
  18. values('OP-300','Operations-MD','Maryland');
  19. insert into department
  20. values('SL-300','Sales-MD','Maryland');
  21. create table employee(
  22. empno varchar(20) primary key,
  23. fullname varchar(50) not null,
  24. address varchar(120),
  25. age int,
  26. salary numeric(8,2) not null,
  27. deptno varchar(20) not null,
  28. constraint fk_employee_department_belong_rltn foreign key(deptno)
  29. references department(deptno)
  30. );
  31. create unique index undx_employee_fullname on employee(fullname);

现在,让我们使用 LINQ to SQL 定义实体的结构和持久性机制。首先创建一个 EmployeeRepository 类来管理员工表的数据访问逻辑。在本例中,您仅需要实现创建操作:


   
     
  1. public class EmployeeRepository {
  2. private HumanResourcesDataContext _ctxHumanResources =
  3. new HumanResourcesDataContext();
  4. public void Create(employee employee) {
  5. this._ctxHumanResources.employees.InsertOnSubmit(employee);
  6. this._ctxHumanResources.SubmitChanges();
  7. }
  8. }

您还需要一个 DepartmentRepository 类来管理部门表的数据访问逻辑。同样,在这个简单的示例中,您仅需要实现读取操作以查找部门列表:


   
     
  1. public class DepartmentRepository {
  2. private HumanResourcesDataContext _ctxHumanResources =
  3. new HumanResourcesDataContext();
  4. public IQueryable<department> FindAll() {
  5. return from dept in this._ctxHumanResources.departments
  6. orderby dept.deptname
  7. select dept;
  8. }
  9. }

现在让我们定义体系结构的另一重要部分:控制器。要定义控制器,请在“解决方案资源管理器”窗口中右键单击 Controllers 文件夹,然后选择“添加”|“控制器”。我使用 HumanResourcesController 作为控制器名称。

Ext JS 表示层

现在让我们回到 Ext JS,使用框架构建应用程序的表示层。对于此解决方案,您仅需要导入 ext-all.js 和 \adapter 及 \resources 文件夹。

转到 Site.Master 页面,在 head 元素中添加对 Ext JS 文件的引用,并添加 <asp:ContentPlaceHolder> 标记元素作为各个页面的自定义 JavaScript 和 CSS 代码的容器,如图 4 所示。

图 4 Site.Master


   
     
  1. <head runat="server">
  2. <title><asp:ContentPlaceHolder ID="TitleContent"
  3. runat="server" /></title>
  4. <link href="http://www.cnblogs.com/Content/Site.css" rel="stylesheet"
  5. type="text/css" />
  6. <!-- Include the Ext JS framework -->
  7. <link href="<%= Url.Content("~/Scripts/ext-3.2.1/resources/css/ext-all.css") %>"
  8. rel="stylesheet" type="text/css" />
  9. <script type="text/javascript"
  10. src="<%= Url.Content("~/Scripts/ext-3.2.1/adapter/ext/ext-base.js") %>">
  11. </script>
  12. <script type="text/javascript"
  13. src="<%= Url.Content("~/Scripts/ext-3.2.1/ext-all.js") %>">
  14. </script>
  15. <!-- Placeholder for custom JS and CSS and JS files
  16. for each page -->
  17. <asp:ContentPlaceHolder ID="Scripts" runat="server" />
  18. </head>

现在让我们添加 MVC 体系结构的其他重要部分:视图。视图将显示窗体,以获取与某位员工相关的数据。转到 HumanResourcesController,右键单击 Index 操作方法,然后选择 Add View。单击 Add View 对话框中的 Add 按钮。

要实现之前在本文中创建的 Ext JS 窗体,您需要在 Scripts 目录中添加一个 JavaScript 文件,并在视图中添加一个对此 JavaScript 文件的引用。然后将该引用添加到 employee_form.js 文件中,并将 div 元素添加到 Index.aspx 视图中(请参见图 5)。

图 5 添加员工窗体


   
     
  1. <%@ Page Title="" Language="C#"
  2. MasterPageFile="~/Views/Shared/Site.Master"
  3. Inherits="System.Web.Mvc.ViewPage" %>
  4. <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent"
  5. runat="server">
  6. Index
  7. </asp:Content>
  8. <asp:Content ID="Content2" ContentPlaceHolderID="MainContent"
  9. runat="server">
  10. <h2>Add a New Employee</h2>
  11. <div id="employeeform"></div>
  12. </asp:Content>
  13. <asp:Content ID="Content3" ContentPlaceHolderID="Scripts"
  14. runat="server">
  15. <script type="text/javascript"
  16. src="<%= Url.Content("~/Scripts/employee_form.js") %>">
  17. </script>
  18. </asp:Content>

转到 employee_form.js 文件,添加一些代码来配置 ExtJS 窗体及其基础小组件。第一步是定义 Ext.data.JsonStore 类的一个实例,以获取部门列表:


   
     
  1. var departmentStore = new Ext.data.JsonStore({
  2. url: 'humanresources/departments',
  3. root: 'departments',
  4. fields: ['deptno', 'deptname']
  5. });

url 属性指向 HumanResourceController 控制器中的 departments 操作方法。此方法通过 HTTP POST 动词进行访问。root 属性是部门列表的根元素。fields 属性指定数据字段。现在定义窗体。这些属性均为自描述性属性:


   
     
  1. var form = new Ext.FormPanel({
  2. title: 'Add Employee Form',
  3. renderTo: 'employeeform',
  4. width: 400,
  5. url: 'humanresources/addemployee',
  6. defaults: { xtype: 'textfield' },
  7. bodyStyle: 'padding: 10px',

在本例中,url 属性指向 HumanResourceController 控制器中的 AddEmployee 操作方法。此方法也可通过 HTTP POST 动词进行访问。

items 属性提供代表窗体字段的小组件列表(图 6)。此处的默认小组件是文本字段(这在 defaults 属性中指定)。第一个字段是员工编号,该字段为必填字段(由 allowBlank 属性指定)。第二个字段是全名,它也是一个必填文本字段。Address 字段是一个可选的文本区域。Age 字段是一个可选的数值字段。Salary 字段是一个必填的数值字段。最后,部门编号字段是一个是从部门列表中选择的标识符字符串。

图 6 窗体字段小组件


   
     
  1. items: [
  2. { fieldLabel: 'Employee ID', name: 'empno', allowBlank: false },
  3. { fieldLabel: 'Fullname', name: 'fullname', allowBlank: false },
  4. { xtype: 'textarea', fieldLabel: 'Address', name: 'address',
  5. multiline: true },
  6. { xtype: 'numberfield', fieldLabel: 'Age', name: 'age' },
  7. { xtype: 'numberfield', fieldLabel: 'Salary', name: 'salary',
  8. allowBlank: false },
  9. { xtype: 'combo', fieldLabel: 'Department', name: 'deptno',
  10. store: departmentStore, hiddenName: 'deptno',
  11. displayField: 'deptname', valueField: 'deptno', typeAhead: true,
  12. mode: 'remote', forceSelection: true, triggerAction: 'all',
  13. emptyText: 'Please, select a department...', editable: false }
  14. ],

最后,将 buttons 属性定义为处理对窗体执行的操作。这里的配置和图 2 中的配置相似,但文本属性的值为“Add”。

现在 employee_form.js 文件已完成。(到目前为止,我已经介绍了此文件的大部分元素。有关此文件的完整源代码列表,请参见代码下载。)

现在让我们转到 HumanResourceController,并执行相应的操作方法,如图 7 所示。

图 7 HumanResourceController


   
     
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. using HumanResources_ExtJS_ASPNETMVC.Models;
  7. namespace HumanResources_ExtJSASPNETMVC.Models.BusinessObjects {
  8. public class HumanResourcesController : Controller {
  9. DepartmentRepository _repoDepartment = new DepartmentRepository();
  10. EmployeeRepository _repoEmployee = new EmployeeRepository();
  11. // GET: /HumanResources/
  12. public ActionResult Index() {
  13. return View();
  14. }
  15. // POST: /HumanResource/Departments
  16. [HttpPost]
  17. public ActionResult Departments() {
  18. var arrDepartment = this._repoDepartment.FindAll();
  19. var results = (new {
  20. departments = arrDepartment
  21. });
  22. return Json(results);
  23. }
  24. // POST: /HumanResource/AddEmployee
  25. [HttpPost]
  26. public ActionResult AddEmployee(employee employee) {
  27. string strResponse = String.Empty;
  28. try {
  29. this._repoEmployee.Create(employee);
  30. strResponse = "{success: true}";
  31. }
  32. catch {
  33. strResponse = "{success: false, error: \"An error occurred\"}";
  34. }
  35. return Content(strResponse);
  36. }
  37. }
  38. }

大功告成!

现在运行解决方案。您将看到如图 8 所示的 Web 页面。在窗体中输入一些数据,然后单击 Add。您会看到一个确认消息框。您还会看到在数据库的 dbo.employee 表中插入的行。

使用 ASP.NET MVC 和 Ext JS 构建以数据为中心的 Web 应用程序

图 8 运行应用程序

创建简单的 RIA 的确就这么简单。根据您想利用的功能,可以使用任何其他常见的 JavaScript 框架并同时使用 ASP.NET MVC 来构建相似的应用程序。您可以轻松地将实体框架替换为数据层,并使用 Windows Azure 存储或 SQL Azure 作为后端数据存储。这些简单的构造块使构建以数据为中心的基本 RIA 变得快速而简单。

你可能感兴趣的:(asp.net)