文章系列: 第一部分第二部分(本文)第三部分 在第一部分中,我们使用了一个基本的AngularJS/ASP。NET MVC应用程序包含以下内容: 构建一个成熟的单页面应用程序的样板代码需要一些基本的路由,包括一个包含参数的路由,以及另一个只有经过身份验证的用户(使用基于cookie的身份验证)才能访问的登录和注册表单的路由,包括使这些功能发挥作用所需的angular Javascript代码 由于懒惰/时间的限制,我在本文的第一部分末尾附上了Visual Studio解决方案。但是,我已经删除了包目录,使其更小,所以您将需要使用Nuget包还原来将它们恢复。 我们出色的应用程序仍然有很多问题,其中一些问题我们将在第二部分中解决。本文比第1部分更短(也不那么令人兴奋),但是将为第3部分准备好所有内容。 源代码 本文附带的源代码可以在这里找到。 第二部分我们要解决的问题 丑陋的URL -当我们在视图之间导航时,我们会在URL中得到这个丑陋的苏格兰标记(#)。我们将在第二部分解决这个问题通过使用HTML5模式(pushstate)丑陋的观点——我们甚至没有尝试任何样式应用到我们的应用程序的一部分,让我们加入Twitter引导和角UI为引导指令添加功能,而不需要jquery非常基本的路由,在第一部分我们使用ngRoute基本路由是好的,但低于当我们有更高级的需求。在第二部分中,我们将用Angular UI Router替换它 丑陋的URL 现在我们的URL看起来是这样的: web服务器将忽略scotch(#)之后的所有内容。我们在第1部分中添加的ngRoute目前接受URL的这一部分,检查它是否与我们设置的任何模式匹配,如果匹配,则将正确的视图加载到登录页面上的容器div中。 如果我们使用AngularJS的HTML5模式,我们可以有更好的URL。这将导致Angular使用HTML5的历史API来处理所有的复杂性,而在我们这端只需要几行代码(一行Javascript和几行c#)。 首先,我们需要修改我们的应用的配置函数,它现在有Angular's $locationProvider模块作为一个依赖项,我们称之为hashPrefix和html5mode函数。就是这样。 隐藏,收缩,复制Code
var configFunction = function ($routeProvider, $httpProvider, $locationProvider) { $locationProvider.hashPrefix('!').html5Mode(true); $routeProvider. when('/routeOne', { templateUrl: 'routesDemo/one' }) .when('/routeTwo/:donuts', { templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; } }) .when('/routeThree', { templateUrl: 'routesDemo/three' }) .when('/login', { templateUrl: '/Account/Login', controller: LoginController }) .when('/register', { templateUrl: '/Account/Register', controller: RegisterController }); $httpProvider.interceptors.push('AuthHttpResponseInterceptor'); } configFunction.$inject = ['$routeProvider', '$httpProvider', '$locationProvider'];
我们登陆页面上的链接也需要更新,以删除苏格兰: 隐藏,复制Code
<ul> <li><a href="/routeOne">Route One</a></li> <li><a href="/routeTwo/6">Route Two</a></li> <li><a href="/routeThree">Route Three</a></li> </ul> <ul> <li><a href="/login">Login</a></li> <li><a href="/register">Register</a></li> </ul>
很好,现在让我们调试站点并浏览: 我们的URL看起来更好,但是点击刷新: HTML5模式正在工作,但只是以一种非常表面的方式。刷新页面会将完整的URL发送到服务器(因为我们已经删除了scotch),而服务器不知道该做什么。我们可以通过正确地重新配置MVC的RouteCollection来解决这个问题。我们需要明确每个视图的路由,然后添加一个all - catch,它会将其他所有URL发送到我们的着陆页面,由Angular处理。 更新App_Start =>中的RegisterRoutes方法RouteConfig.cs一样: 隐藏,收缩,复制Code
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "routeOne", url: "routesDemo/One", defaults: new { controller = "RoutesDemo", action = "One" }); routes.MapRoute( name: "routeTwo", url: "routesDemo/Two/{donuts}", defaults: new { controller = "RoutesDemo", action = "Two", donuts = UrlParameter.Optional }); routes.MapRoute( name: "routeThree", url: "routesDemo/Three", defaults: new { controller = "RoutesDemo", action = "Three" }); routes.MapRoute( name: "login", url: "Account/Login", defaults: new { controller = "Account", action = "Login" }); routes.MapRoute( name: "register", url: "Account/Register", defaults: new { controller = "Account", action = "Register" }); routes.MapRoute( name: "Default", url: "{*url}", defaults: new { controller = "Home", action = "Index" }); }
再次调试网站,浏览周围,并测试后退/刷新按钮,一切现在应该正常工作。 丑陋的观点 现在的网站没有任何样式应用。让我们用Twitter Bootstrap来解决这个问题,我将在Cloudflare中添加它。和Angular UI引导指令一起,我将在关闭页面之前添加这些指令。标签。 只需应用几个CSS类,并添加一些元素,就可以改变整个web应用程序的外观。像这样更新你的登陆页面: 隐藏,收缩,复制Code
<!DOCTYPE html> <html ng-app="AwesomeAngularMVCApp" ng-controller="LandingPageController"> <head> <title ng-bind="models.helloAngular"></title> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/css/bootstrap.min.css"> @Styles.Render("~/Content/css") </head> <body> <div class="navbar navbar-default navbar-fixed-top" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" ng-click="navbarProperties.isCollapsed = !navbarProperties.isCollapsed"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Awesome Angular MVC APP</a> </div> <div class="navbar-collapse collapse" collapse="navbarProperties.isCollapsed"> <ul class="nav navbar-nav"> <li><a href="/routeOne">Route One</a></li> <li><a href="/routeTwo/6">Route Two</a></li> <li><a href="/routeThree">Route Three</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="/login">Login</a></li> <li><a href="/register">Register</a></li> </ul> </div> </div> </div> <div class="container mainContent"> <div ng-view></div> </div> <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.10.0/ui-bootstrap-tpls.min.js"></script> @Scripts.Render("~/bundles/AwesomeAngularMVCApp") </body> </html>
将以下内容添加到Content =>Site.css 隐藏,复制Code
.mainContent { margin-top: 60px; }
现在,我们需要在Angular应用模块中为Bootstrap模块注册Angular UI指令,我们在AwesomeAngularMVCApp.js中这样做: 隐藏,复制Code
var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', ['ngRoute', 'ui.bootstrap']);
最后,我们的Angular着陆页面控制器需要更新为移动导航菜单的默认状态(最初会被折叠): 隐藏,复制Code
var LandingPageController = function($scope) { ... $scope.navbarProperties = { isCollapsed: true }; }
Twitter Bootstrap本身就是一个完整的主题。本节的重点是向您展示如何正确地将其添加到任何AngularJS应用程序中。 在不使用jQuery的情况下,将移动导航菜单设置为正确地展开和折叠,这是很多人都会遇到的问题,就像在应用模块中正确地注册Angular UI引导指令一样,所以我在这里强调了这些问题。 现在你可以在此基础上使用Twitter引导程序来完全设计你的站点。 非常基本的路由 我们目前使用的是AngularJS自己的ngRoute模块,这对于基本的路由是很好的,但是如果我们有更高级的路由需求,我们可能会发现outself有点受限ts。 让我们用Angular UI路由器来替换它。这是一个完全成熟的Angular路由框架,它为我们提供了嵌套的、多视图和命名视图。 我们现在已经完成了足够的准备工作,但是如果您计划使用它来构建一个完整的单页应用程序,那么您需要阅读深入指南,并将API引用保存在附近的选项卡中以供参考。 更新你的登陆页面,将添加ngRoute的Javascript标签替换为Angular UI路由器的标签: 隐藏,复制Code
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.min.js"></script>
现在我们需要在Angular应用模块中注册Angular UI路由器,移除ngRoute的注册: 隐藏,复制Code
var AwesomeAngularMVCApp = angular.module('AwesomeAngularMVCApp', ['ui.router', 'ui.bootstrap']);
Angular UI路由器是基于状态的。它基于有限状态机的数学概念,并将您的web应用程序转换为相同的有限状态机。不是从一个URL导航到另一个URL,而是从一个状态转换到另一个状态,并设置一个路由来表示应用程序可能处于的每个状态。 在UI Router中,我们可以在着陆页面上有多个容器视图,而在ngRoute中,我们只能有一个。现在我们将添加两个视图到我们的着陆页面,并设置我们的应用程序有四个状态: 当应用程序状态,我们将负载路线到我们第一个容器div,和路线两个到我们的第二状态两个应用程序时,我们将负载路线到我们第一个容器div,和路线三到我们的第二状态三个应用程序时,我们将路线两个加载到第一个容器div,和路线三到我们第二LoginRegister状态中的应用程序时,我们将我们的登录表单加载到第一个容器div,并将注册表转入第二行 更新登陆页,这样我们现在有两个容器div,删除容器div,我们以前使用ngRoute: 隐藏,复制Code
<div class="container mainContent"> <div class="row"> <div class="col-md-6"> <div ui-view="containerOne"></div> </div> <div class="col-md-6"> <div ui-view="containerTwo"></div> </div> </div> </div>
现在让我们修改AwesomeAngularMVCApp.js来告诉它把什么视图放在什么位置,在什么状态下。我们不再依赖ngRoute的$routeProvider 服务,而有一个新的依赖于UI路由器的$stateProvider: 隐藏,收缩,复制Code
var configFunction = function ($stateProvider, $httpProvider, $locationProvider) { $locationProvider.hashPrefix('!').html5Mode(true); $stateProvider .state('stateOne', { url: '/stateOne?donuts', views: { "containerOne": { templateUrl: '/routesDemo/one' }, "containerTwo": { templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; } } } }) .state('stateTwo', { url: '/stateTwo', views: { "containerOne": { templateUrl: '/routesDemo/one' }, "containerTwo": { templateUrl: '/routesDemo/three' } } }) .state('stateThree', { url: '/stateThree?donuts', views: { "containerOne": { templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; } }, "containerTwo": { templateUrl: '/routesDemo/three' } } }) .state('loginRegister', { url: '/loginRegister?returnUrl', views: { "containerOne": { templateUrl: '/Account/Login', controller: LoginController }, "containerTwo": { templateUrl: '/Account/Register', controller: RegisterController } } }); $httpProvider.interceptors.push('AuthHttpResponseInterceptor'); } configFunction.$inject = ['$stateProvider', '$httpProvider', '$locationProvider'];
我们还需要更新AuthHttpResponseInterceptor,使其在服务器返回401响应时进入loginRegister状态。要实现这一点,我们需要注入UI路由器的$state服务。然而,由于这个库中的一个bug,我们不能直接注入它。相反,我们注入了AngularJS的$injector服务,并用它来解析一个$state的实例: 隐藏,复制Code
var AuthHttpResponseInterceptor = function($q, $location, $injector) { return { response: function (response) { if (response.status === 401) { console.log("Response 401"); } return response || $q.when(response); }, responseError: function (rejection) { if (rejection.status === 401) { $injector.get('$state').go('loginRegister', { returnUrl: $location.path() }); } return $q.reject(rejection); } } } AuthHttpResponseInterceptor.$inject = ['$q', '$location', '$injector'];
我们的LoginController也需要更新,因为它现在从UI路由器的$stateParams对象中提取返回的URL,而不是ngRoute的$routeParams对象: 隐藏,复制Code
var LoginController = function ($scope, $stateParams, $location, LoginFactory) { $scope.loginForm = { ...etc returnUrl: $stateParams.returnUrl, ...etc }; ...etc } LoginController.$inject = ['$scope', '$stateParams', '$location', 'LoginFactory'];
最后,我们需要更新链接。有趣的是,我们的超链接不再以URL为中心,现在我们直接链接到状态本身,并使用JSON提供该状态的任何参数。更新登陆页面上的链接,使其看起来像这样: 隐藏,复制Code
<div class="navbar-collapse collapse" collapse="navbarProperties.isCollapsed"> <ul class="nav navbar-nav"> <li><a ui-sref="stateOne({ donuts: 12 })">State One</a></li> <li><a ui-sref="stateTwo">State Two</a></li> <li><a ui-sref="stateThree({ donuts: 4 })">State Three</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a ui-sref="loginRegister">Login / Register</a></li> </ul> </div>
现在让它进行调试和浏览。航行到一个应该返回路线1和2,并排: 如果我们尝试导航到状态2或状态3,拦截器将像以前一样介入,并将我们转换到loginregisterstate。如果我们在此时登录,我们还可以查看状态2和状态3。 嵌套视图 我们还没有讨论嵌套视图。让我们向我们的RoutesDemo控制器添加另一个c#动作方法Four,并使用Visual Studio创建视图。向此视图添加一些内容以惟一地标识它。现在还要从路由3中删除Authorize属性,这只是为了演示一个概念。 我们将把路径4嵌套在路径1里面,所以像这样更新路径1的视图: 隐藏,复制Code
Route one <div ui-view="nestedView"></div>
现在让我们更新Angular中的路由配置来反映这一点。在向状态添加嵌套视图时,我们使用命名配置viewName@stateName。为了为stateOne配置nestedView我们给它命名为nestedView@stateOne,像这样: 隐藏,复制Code
$stateProvider .state('stateOne', { url: '/stateOne?donuts', views: { "containerOne": { templateUrl: '/routesDemo/one' }, "containerTwo": { templateUrl: function (params) { return '/routesDemo/two?donuts=' + params.donuts; } }, "nestedView@stateOne": { templateUrl: '/routesDemo/four' } } }) .state('stateTwo', ...etc
我们刚刚添加了一个新视图,因此需要更新routeconfig来反映这一点: 隐藏,复制Code
routes.MapRoute( name: "routeFour", url: "routesDemo/Four", defaults: new { controller = "RoutesDemo", action = "Four" });
现在让我们来测试一下: 回顾 在第二部分中我们实现了以下内容: 在AngularJS中启用HTML5模式(pushstate),必要时配置MVC,添加Twitter Bootstrap和Angular UI引导。现在我们可以从这些库中添加组件了,我们需要用Angular UI router替换ngRoute,并在应用程序中添加命名、多视图和嵌套视图。 接下来是第三部分 信号集成指令反伪造令牌 评论/批评/问题等 如果有任何评论/批评/问题,请对这篇文章发表评论,我会回复你的。谢谢阅读:) 本文转载于:http://www.diyabc.com/frontweb/news1678.html