[译]用AngularJS构建大型ASP.NET单页应用(一)
原文地址:http://www.codeproject.com/Articles/808213/Developing-a-Large-Scale-Application-with-a-Single
渣译,各位看官请勿喷
引言:
...
单页面应用程序(SPA),被定义为在一个独立的页面上提供类似于桌面应用程序级用户体验为目标的网站。在SPA, 基本上所有的代码 - 例如 HTML,JavaScript和CSS - 都是在响应用户操作时动态加载的。页面没有在任何时候被重新刷新,也没有跳转到另一个页面,但现代WEB技术(例如HTML5)可以提供类似单独页面一样的导航。单页面应用程序往往需要与后台Web服务器动态交互。
...
本文的目标是开发出能支撑大量用户使用的丰富内容的企业级单页面应用程序,其包括认证,授权,回话等功能。
概述 - AngularJS
本文的示例应用程序包含的创建和更新用户帐户,创建和更新客户和产品功能。另外,该应用程序还允许你查询、创建和更新销售订单。该示例应用程序将使用AngularJS建成。 AngularJS是一个开源Web应用框架, 其社区由谷歌维护的。
AngularJS使你创建单页面应用程序只需要包含HTML,CSS和JavaScript等客户端脚本。它的目标是增强Web应用程序的模型 - 视图 - 控制器(MVC)的能力,使开发和测试更容易。
该库读取HTML中自定义标签的属性,通过这些自定义属性,绑定输入、输出功能的JavaScript model变量。这些JavaScript变量可以被手动设置,也可以从JSON中获取。
AngularJS入门-模板页、模块、路由
你需要做的第一件事情就是下载AngularJS框架到项目中。你可以在https://angularjs.org的AngularJS框架。本文的示例应用程序使用Web Express 2013 开发,所以这里通过NuGet安装AngularJS包...
我创建了一个空的Visual Studio Web应用程序项目,并选择了Microsoft Web API 2库。此应用程序将使用Web API 2库提供REST风格的接口服务。
现在,你需要做两件事情要来构建一个AngularJS单页面应用程序:建立模板页面,并设置相关路由。首先,模板页只需要一个引用AngularJS JavaScript库并增加ng-view 指令告诉AngularJS哪些地方需要加载其他内容。
<!DOCTYPE html>
<html lang="en">
<head>
<title>AngularJS Shell Page example</title>
</head>
<body>
<div>
<ul>
<li><a href="#Customers/AddNewCustomer">Add New Customer</a></li>
<li><a href="#Customers/CustomerInquiry">Show Customers</a></li>
</ul>
</div>
<!-- ng-view directive to tell AngularJS where to inject content pages -->
<div ng-view></div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>
在上面的模板页中,链接被映射到AngularJS路由。div标签中的ng-view指令,通过AngularJS $route service加载不同的内容至模板页中。 例如,如果用户选择“Add New Customer”链接,AngularJS会令其中ng-view指令存在的div标签内插入添加客户的内容。呈现的内容是部分HTML页面。
同时app.js JavaScript也需要在模板被引用。 这个JS文件将会为应用程序创建一个AngularJS模块。此外,对于路由配置也将在本文件中被定义。你可以把AngularJS模块作为一个应用程序的不同部分。AngularJS应用程序没有main方法。模块需要声明指定的应用程序如何配置。本文的示例应用程序将只能有一个AngularJS模块,它包含了客户、产品、订单和用户等应用程序的几个不同部分。
现在,下面的app.js文件的主要目的是建立AngularJS路由。AngularJS $routeProvider服务使用when()方法来匹配URI。当匹配成功时,页面的部分HTML内容被加载到模板页,并关联到对应的controller文件。controller文件很简单,就是处理指定路由请求的JavaScript 文件。
//Define an angular module for our app
var sampleApp = angular.module('sampleApp', []);
//Define Routing for the application
sampleApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/Customers/AddNewCustomer', {
templateUrl: 'Customers/AddNewCustomer.html',
controller: 'AddNewCustomerController'
}).
when('/Customers/CustomerInquiry', {
templateUrl: 'Customers/CustomerInquiry.html',
controller: 'CustomerInquiryController'
}).
otherwise({
redirectTo: '/Customers/AddNewCustomer'
});
}]);
AngularJS Controllers
AngularJS控制器是一个的JavaScript函数。控制器用于给你的视图添加业务逻辑。视图是HTML页面。这些页面将显示出被双向数据绑定的数据。控制器的负责将数据传输给视图。
<div ng-controller="customerController">
<input ng-model="FirstName" type="text" style="width: 300px" />
<input ng-model="LastName" type="text" style="width: 300px" />
<div>
<button class="btn btn-primary btn-large" ng-click="createCustomer()"/>Create</button>
对于上述AddCustomer模板,ng-controller指令将调用JavaScript函数中的customerController方法,并进行数据绑定。
function customerController($scope)
{
$scope.FirstName = "William";
$scope.LastName = "Gates"; $scope.createCustomer = function () {
var customer = $scope.createCustomerObject();
customerService.createCustomer(customer,
$scope.createCustomerCompleted,
$scope.createCustomerError);
}
}
可扩展性性
当我开发了本文的示例程序,单页面应用程序的前两个可扩展性问题变得越来越明显了。AngularJS需要在页面上引用并下载所有相关JavaScript文件。一个大型的应用程序可能包含数百个JavaScript文件,这似乎并不理想。另一个问题是当我用AngularJS路由表,每一个路由规则我都要写到路由表中,当路由表中有数百个规则时,这显然不是一个好的办法。
使用RequireJS动态加载javascript文件
对于此示例,我不想在模板页上加载所有的JavaScript文件。大型应用程序通常需要数百个JavaScript文件。一般情况下,JavaScript文件是逐一使用script标签加载的。此外,每个文件有可能依赖于其他文件。RequireJS这个JavaScript库正可以动态加载JavaScript文件。
RequireJS是一个著名的JavaScript模块和文件加载器,它支持在主流浏览器。使用RequireJS时,需要将JavaScript代码分割成各个模块,每个文件负责单一的功能。此外,我们还可以配置相应文件的依赖关系。
RequireJS提供了一个简洁的方式来加载和管理你的Javascript应用程序。
AngularJS路由转换
在AngularJS中,你需要为不同的规则配置不同的路由。 我决定来统一网页和相关的JavaScript文件的命名约定,并允许应用程序解析路由的名称,自动绑定将JS方法和页面绑定。
例如,客户维护内容页被命名为CustomerMaintenance.Html,AngularJS控制器对应的JavaScript文件被命名为CustomerMaintenanceController.js。
让我们开始修改示例应用程序。首先,每一个应用程序都需要某种形式的认证和授权机制来控制权限。此应用程序将使用ASP.NET Forms Authentication进行身份验证。一旦通过验证,用户将能够访问应用程序的其余部分。一般网站都会有不同的母版页,一个用 于显示的登录页面,另一个母版页通常包含一个主菜单栏、边栏附和头部等菜单选项,内容区域和页脚区域。此示例应用程序通过多个模板页面来实现。登录成功后,用户将被路由到一个新的模板页面。
多模板页
第一个模板页是index.html。此页面将显示登录和用户注册的相关内容。正如你所看到的,这里只引用了一个JavaScript文件。 Main.js将包含RequireJS的配置信息来动态加载模块。我们将index.html的AngularJS控制器命名为indexController.js。如果用户成功注 册或登录,该应用程序将跳转到一个新的模板页面applicationMasterPage.html。在模板页上, 有一个ng-view指令。如前所述,这个指令会告诉AngularJS内容被将被加载到哪里。
<!-- index.html --> <!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> </title>
<script data-main="main.js" src="Scripts/require.js"> </script>
<link href="Content/angular-block-ui.css" rel="stylesheet" />
<link href="Content/bootstrap.css" rel="stylesheet" />
<link href="Content/Application.css" rel="stylesheet" />
<link href="Content/SortableGrid.css" rel="stylesheet" />
</head>
<body ng-controller="indexController" ng-init="initializeController()" >
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-collapse collapse" id="MainMenu">
<ul class="nav navbar-nav" ng-repeat="menuItem in MenuItems">
<li> <a href="{{menuItem.Route}}">{{menuItem.Description}} </a> </li>
</ul>
</div>
</div>
</div>
<!-- ng-view directive to tell AngularJS where to put the content pages-->
<div style="margin: 75px 50px 50px 50px" ng-view> </div>
</body>
</html>
Main.js - RequireJS设置和配置文件
此应用程序将使用RequireJS异步加载脚本和JS的依赖管理。如先前所示,模板页面将只引用main.js。这是RequireJS的配置文件。在下面的JavaScript文件有三个部分。
第一部分定义了所有需要的用于装载的JavaScript文件和模块的路径。由于RequireJS只加载JavaScript文件,所以不需要添加.js的后缀。
第二部分定义了shim 部分。shim 构造了允许RequireJS加载非AMD的兼容脚本。
第三部分bootstraps等初始化脚本。
// main.js require.config({
baseUrl: "",
// alias libraries paths
paths: {
'application-configuration': 'scripts/application-configuration',
'angular': 'scripts/angular',
'angular-route': 'scripts/angular-route',
'angularAMD': 'scripts/angularAMD',
'ui-bootstrap' : 'scripts/ui-bootstrap-tpls-0.11.0',
'blockUI': 'scripts/angular-block-ui',
'ngload': 'scripts/ngload',
'mainService': 'services/mainServices',
'ajaxService': 'services/ajaxServices',
'alertsService': 'services/alertsServices',
'accountsService': 'services/accountsServices',
'customersService': 'services/customersServices',
'ordersService': 'services/ordersServices',
'productsService': 'services/productsServices',
'dataGridService': 'services/dataGridService',
'angular-sanitize': 'scripts/angular-sanitize',
'customersController': 'Views/Shared/CustomersController',
'productLookupModalController': 'Views/Shared/ProductLookupModalController'
},
// Add angular modules that does not support AMD out of the box, put it in a shim
shim: {
'angularAMD': ['angular'],
'angular-route': ['angular'],
'blockUI': ['angular'],
'angular-sanitize': ['angular'],
'ui-bootstrap': ['angular'] },
// kick start application
deps: ['application-configuration']
});
Application-Configuration.js - Bootstrap等配置文件
AngularJS有两个执行阶段,配置阶段和运行阶段。Application-Configuration.js将执行AngularJS配置阶段。我们将设置使用AngularJS routeProvider服务。
// application-configuration.js "use strict";
define(['angularAMD', 'angular-route', 'ui-bootstrap', 'angular-sanitize', 'blockUI', ],
function (angularAMD) { var app = angular.module("mainModule",
['ngRoute', 'blockUI', 'ngSanitize', 'ui.bootstrap']); app.config(['$routeProvider', function ($routeProvider) { $routeProvider .when("/", angularAMD.route({ templateUrl: function (rp) { return 'Views/Main/default.html'; },
controllerUrl: "Views/Main/defaultController"
})) .when("/:section/:tree", angularAMD.route({ templateUrl: function (rp) {
return 'views/' + rp.section + '/' + rp.tree + '.html'; }, resolve: {
load: ['$q', '$rootScope', '$location',
function ($q, $rootScope, $location) { var path = $location.path();
var parsePath = path.split("/");
var parentPath = parsePath[1];
var controllerName = parsePath[2];
var loadController = "Views/" + parentPath + "/" +
controllerName + "Controller"; var deferred = $q.defer();
require([loadController], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
})) .when("/:section/:tree/:id", angularAMD.route({ templateUrl: function (rp) {
return 'views/' + rp.section + '/' + rp.tree + '.html'; }, resolve: {
load: ['$q', '$rootScope', '$location',
function ($q, $rootScope, $location) {
var path = $location.path();
var parsePath = path.split("/");
var parentPath = parsePath[1];
var controllerName = parsePath[2];
var loadController = "Views/" + parentPath + "/" +
controllerName + "Controller"; var deferred = $q.defer();
require([loadController], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
}))
.otherwise({ redirectTo: '/' })
}]); // Bootstrap Angular when DOM is ready
angularAMD.bootstrap(app); return app;
});
定义RequireJS
打开application-configuration.js文件,我们会看到一个定义函数。定义函数是RequireJS的加载代码模块。requireJS定义模块与传统一个很大的不同是他可以保证其定义的变量处于某个范围内,从而避免了全局命名污染。他能明确的罗列出其依赖,并且在那些依赖上找到处理办法,而不是必须对那些依赖指定全局变量。
此应用程序依赖angularAMD, angular-route, ui-bootstrap, angular-sanitize和blockUI。
AngularAMD, UI-Bootstrap, Angular-Sanitize and BlockUI
Application-Configuration.js依赖angularAMD。angularAMD是有利于支持按需加载控制器和第三方模块,如Angular-UI。
UI-Bootstrap 是包含一套基于引导的标记和CSS本地AngularJS指令的存储库。此应用程序引用Angular-UI和Twitter的Bootstrap CSS的样式。
angular-sanitize库允许HTML被注入到视图模板。默认情况下,AngularJS防止注入的HTML标签作为一种安全措施.这个应用程序使用AngularJS blockUI配置库,允许您阻塞在AJAX请求时的用户交互。
动态路由表
application-configuration.js 的最大目的是为内容页面和与之关联的JavaScript控制器设置路由、渲染和加载规则。探索如何使用约定而不是硬编码的方式来创建动态路由表。在这次探险过程中我发现了Per Ploug's的博客http://scriptogr.am/pploug/post/convention-based-routing-in-angularjs。在他的博客中他提到了路由的下面这些元素,这些元素可以从AngularJS的的路由提供器中获得:
/:secion/:tree/:action/:id
在示例中,大部分网页文件在Views文件夹下。 Veiws文件夹中,一个模块对应一个子文件夹,如Accounts, Customers, Orders, Products等。 修改用户页面的根路径是 /Views/Customers/CustomerMaintenance, 查询订单页面的根路径是/Views/Orders/OrderInquiry.为了方便控制器动态加载文件,我把这些页面的控制器代码文件也放到Views文件夹下。
修改用户页面的控制器文件路径是 /Views/Customers/CustomerMaintenanceController.js,这样可以简化开发。把公共的代码放到工程的同一个文件夹下,可以让你快速定位需要查看的代码。 在MVC框架里,控制器文件通常被单独放在一个文件夹下,当工程变得比较庞大时,这些文件会难以维护。
渲染HTML模板很简单。 只需设置一下templateUrl属性:
'views/' + rp.section + '/' + rp.tree + '.html'.
引入 rp.setion和 rp.tree变量,可以很容易实现路径匹配、路径转换。转换完路径后,唯一需要做的事是把扩展名 .html连接到字符串末尾。
加载控制器文件的过程有点复杂。 AngularJS路径配置的控制器属性只支持静态的字符串。 它不支持含有变量的字符串,如下:
controller = "Views/" + parentPath + "/" + controllerName + "Controller";
经过一段时间的研究,我发现可以通过功能分解来设置控制器属性。 结合使用AngularJS的location service和deferred promise特性,我最终实现动态加载js控制器文件时设置控制器属性值。 js性能的一个提升意味着这次改造产生了最终的价值。
路由表里最终只有两个主路径,AngularJS需要对其进行匹配。第二个路径
/:section/:tree/:id
是用来处理那些带有参数的路径的。现在,不管应用变得多大,路由表都将会保持的很小,而且只需要跟两个路径进行匹配,这样就提高了路由匹配的效率。
最终,application-configuration.js使用angularAMD来引导AngularJS应用。
[译]用AngularJS构建大型ASP.NET单页应用(一)的更多相关文章
- [译]用AngularJS构建大型ASP.NET单页应用(二)
原文地址:http://www.codeproject.com/Articles/808213/Developing-a-Large-Scale-Application-with-a-Single 客 ...
- [译]用AngularJS构建大型ASP.NET单页应用(三)
原文地址:http://www.codeproject.com/Articles/808213/Developing-a-Large-Scale-Application-with-a-Single A ...
- 使用AngularJS构建大型Web应用
AngularJS是由Google创建的一种JS框架,使用它可以扩展应用程序中的HTML词汇,从而在web应用程序中使用HTML声明动态内容.在该团队工作的软件工程师Brian Ford近日撰写了一篇 ...
- AngularJS开发指南16:AngularJS构建大型Web应用详解
AngularJS是由Google创建的一种JS框架,使用它可以扩展应用程序中的HTML功能,从而在web应用程序中使用HTML声明动态内容.在该团队工作的软件工程师Brian Ford近日撰写了一篇 ...
- 基于Vue2.0+Vue-router构建一个简单的单页应用
爱编程爱分享,原创文章,转载请注明出处,谢谢!http://www.cnblogs.com/fozero/p/6185492.html 一.介绍 vue.js 是 目前 最火的前端框架,vue.js ...
- (译) 在AngularJS中使用的表单验证功能【转】
验证功能是AngularJS里面最酷炫的功能之一,它可以让你写出一个具有良好用户体验的Web应用. 在AngularJS中,有许多用于验证的指令.我们将先学习几个最流行的内置指令,然后再创建一个自定义 ...
- (译) 在AngularJS中使用的表单验证功能
验证功能是AngularJS里面最酷炫的功能之一,它可以让你写出一个具有良好用户体验的Web应用. 在AngularJS中,有许多用于验证的指令.我们将先学习几个最流行的内置指令,然后再创建一个自定义 ...
- 使用 AngularJS 开发一个大规模的单页应用(SPA)
本文的目标是基于单页面应用程序开发出拥有数百页的内容,包括认证,授权,会话状态等功能,可以支持上千个用户的企业级应用. 下载源代码 介绍 (SPA)这样一个名字里面蕴含着什么呢? 如果你是经典的S ...
- 快速构建一个简单的单页vue应用
技术栈 vue-cli webpack vux,vux-loader less,less-loader vue-jsonp vue-scroller ES6 vue-cli:一个vue脚手架工具,利用 ...
随机推荐
- 生成的API分析文件太大。我们无法在交付前验证您的API使用信息。这只是通知信息。
这次使用了APICloud平台来开发移动APP, 发布的时候在api控制台云编译成ipa后,这次使用apple提供的Application Loader工具提交apa文件到iTunes上去,提交结束的 ...
- n个元素的入栈顺序有多少种出栈顺序?
问题:w1.w2.w3.w4.w5,5个元素将会按顺序入栈,求出栈顺序有多少种情况. 先写一下结论方便记忆: 1个元素:1种 2个元素:2种 3个元素:5种 4个元素:14种 5个元素:42种 简单的 ...
- iOS--浅谈iOS沙盒目录
原文地址:http://blog.csdn.net/wzzvictory/article/details/18269713 出于安全考虑,iOS系统的沙盒机制规定每个应用都只能访问当前沙盒目录下面的文 ...
- 微信小程序 - 开发指南
一.下载并安装开发工具 下载地址 二.创建项目 打开开发工具 添加项目 进入预览和调试界面 代码编辑器 编译并预览 三.启动流程 四.适用场景 五.技术框架 六.科普 [图片较大 - 点击查看]
- [Erlang 0105] Erlang Resources 小站 2013年1月~6月资讯合集
很多事情要做,一件一件来; Erlang Resources 小站 2013年1月~6月资讯合集,方便检索. 小站地址: http://site.douban.com/204209/ ...
- SQL SERVER 监控数据文件增长情况
在项目前期评估数据库的增长情况,然后根据数据库数据量的增长情况来规划存储的分配其实是一件比较麻烦的事情.因为项目没有上线,用什么来评估数据库的数据增长情况呢? 如果手头没有实际的数据,我们只能从表的数 ...
- django ORM
http://www.cnblogs.com/alex3714/articles/5512568.html 常用ORM操作 一.示例Models from django.db import model ...
- php之验证码小程序
验证码功能(个人理解): 减轻服务器的压力(如12306的验证码功能): 防止暴力注册 个人思路:在a-z,A-Z,1-9生成n位随机的数来构成新的验证码. 关于生成验证码的几个小函数 range() ...
- SQL报表(Report Builder)里面的几个常见问题(持续更新)
一 SQL报表常常会遇到在表格中的相除,如果分母为零,一般会显示错误号,我们可以这么处理:(加上是A/B) ,, B) 但是我们不能这么写: ,,A/B) //我们不能这么写,会产生BUG,至于什么B ...
- BOOST.Asio——Tutorial
=================================版权声明================================= 版权声明:原创文章 谢绝转载 啥说的,鄙视那些无视版权随 ...