zoukankan      html  css  js  c++  java
  • 使用AngularJS开发具有单页应用程序(SPA)的大型应用程序

    下载源

    介绍

    名字有什么关系?如果你是经典电视剧《宋飞正传》的粉丝,那么你一定知道唐娜·张这个名字。杰瑞遇到唐娜,,不是中国人,但实际上缩短了她的姓Changstein常同时承担中国stereotypes 如显示针灸感兴趣或有一次念一个词带有中国口音只唐娜与乔治的母亲在电话里给她建议(通过引用孔子)。当乔治把唐娜介绍给他的父母时,乔治的母亲意识到唐娜不是中国人,没有接受她的建议。

    单页应用程序(single-page application, SPA)定义为适合于单个web页面的web应用程序或web站点,其目标是为桌面应用程序提供更流畅的用户体验。在SPA中,所有必需的代码(HTML、JavaScript和CSS)在加载一个页面时被检索,或者适当的资源在需要时被动态加载并添加到页面中,通常是为了响应用户的操作。虽然现代web技术(如HTML5中包含的技术)可以为应用程序中独立的逻辑页面提供感知和导航能力,但页面在过程中的任何时候都不会重新加载,也不会控制转移到另一个页面。与单页面应用程序的交互通常涉及到与后台web服务器的动态通信。 那么这个技术和ASP有什么不同呢?净大师页面?ASP。NET母版页允许您为应用程序中的页面创建一致的布局。单个母版页定义应用程序中所有页面(或一组页面)所需的外观和标准行为。然后,您可以创建包含您想要显示的内容的各个内容页面。当用户请求内容页时,他们将与母版页合并以产生输出,该输出将母版页的布局与内容页的内容结合在一起。 当您开始深入研究单页面应用程序和ASP之间的区别时。网主页实现,你开始意识到他们实际上相似多于不同——一个单页面应用程序只是一个壳页面内容页面就像一个主页,除了壳牌页面在一个单页面应用程序重新加载或执行不像一个主页与每个新页面请求。 也许名字单页面应用程序是一个不幸的名字(比如唐娜Chang),这可能让你相信这项技术并不是一种合适的选择为构建基于web的应用程序,需要扩展到企业级的应用程序可能包含几百个网页内容与成千上万的用户。

    本文的目标是开发一个单页面应用程序,该应用程序可以使用数百个内容页面实现,具有企业应用程序支持数千用户所需的所有功能,包括身份验证、授权和会话状态等。

    概述——AngularJS

    本文的示例应用程序将包含创建和更新用户帐户、创建和更新客户和产品的功能。此外,该应用程序还允许您创建和更新销售订单,包括执行查询所有这些信息的功能。为了实现这一点,示例应用程序将使用AngularJS构建。AngularJS是一个开源的web应用程序框架,由谷歌和一个AngularJS社区的开发者维护。 ,,,,,,, AngularJS帮助创建单页面应用程序,在客户端只需要HTML、CSS和JavaScript。它的目标是用模型-视图-控制器(MVC)功能增强web应用程序,从而使开发和测试都更容易。 该库读取包含额外自定义标签属性的HTML;然后,它遵循这些自定义属性中的指令,并将页面的输入或输出部分绑定到由标准JavaScript变量表示的模型。这些JavaScript变量的值可以手动设置,也可以从静态或动态JSON资源中检索。

    , Image 1

    开始使用AngularJS——外壳页面,模块和路由

    首先需要做的是将AngularJS框架下载到您的项目中。你可以在https://angularjs.org获得AngularJS框架。本文的示例应用程序是使用Microsoft Visual Studio Web Express 2013版开发的,所以我通过执行命令行从Nuget包中安装了AngularJS: 安装包AngularJS 1.2.21版本 在Nuget包管理控制台中,为了保持简单和灵活,我创建了一个空的Visual Studio web应用程序项目,并选择了对Microsoft web API 2库的核心引用。这个应用程序将使用Web API 2库来处理RESTful API服务器请求。 ,,,, 现在,在使用AngularJS构建单页面应用程序时,首先需要做的两件事是设置shell页面和用于检索内容页面的路由表。首先,shell页面只需要一个AngularJS JavaScript库的引用和一个ng-view指令来告诉AngularJS内容页面需要在shell页面的哪个地方呈现。

    <!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>

    在上面的shell页面示例中,链接映射到AngularJS路由。div标签上的ng-view指令是对AngularJS $route服务的一个补充,它将所选路由的呈现内容页包含到shell页中。每次当前路线更改时,包含的视图也会根据$route服务的配置进行更改。例如,如果用户选择了“添加新客户”链接,AngularJS就会在ng-view指令存在的div标签中呈现添加新客户的内容。呈现的内容是HTML的部分页面。 ,,,, 下面的app.js JavaScript文件也在shell页面中被引用。该文件中的JavaScript将为应用程序创建一个AngularJS模块。另外,到应用程序的所有路由的配置将在这个文件中定义。您可以将AngularJS模块看作应用程序不同部分的容器。大多数应用程序都有一个主方法,用于实例化和连接应用程序的不同部分。AngularJS应用程序没有主方法。相反,模块声明式地指定应用程序应该如何引导和配置。本文的示例应用程序只有一个AngularJS模块,尽管该应用程序有几个不同的区域(客户、产品、订单和用户)。

    现在,下面的app.js文件的主要目的是设置AngularJS路由。AngularJS $routeProvider服务接受when()方法,它匹配Uri的模式。当找到匹配项时,将部分页面HTML内容连同内容的相关控制器文件一起加载到shell页面中。控制器文件只是将与指定路由请求的内容一起引用的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控制器

    AngularJS控制器只不过是简单的JavaScript函数,它被绑定到一个特定的范围。控制器用来给你的视图添加逻辑。视图是HTML页面。这些页面只是显示我们使用双向数据绑定绑定到它们的数据。基本上,控制器的职责是将模型(数据)与视图绑定在一起。

    <div ng-controller="customerController">
    <input ng-model="FirstName" type="text" style=" 300px" />
    <input ng-model="LastName" type="text" style=" 300px" />       
    <div>
    <button class="btn btn-primary btn-large" ng-click="createCustomer()"/>Create</button>

    对于上面的AddCustomer模板,ng-controller指令将引用JavaScript函数customerController,该函数将执行视图的所有数据绑定和JavaScript函数。

    function customerController($scope) 
    {
        $scope.FirstName = "William";
        $scope.LastName = "Gates"; 
    
        $scope.createCustomer = function () {          
            var customer = $scope.createCustomerObject();
            customerService.createCustomer(customer, 
                            $scope.createCustomerCompleted, 
                            $scope.createCustomerError);
        }
    }

    开箱即用——可伸缩性问题

    我发展本文的示例应用程序中,前两个成为明显的与一个单页面应用程序可伸缩性问题,that 开箱即用的,AngularJS要求所有应用程序的JavaScript文件和控制器从壳中被引用和下载页面引导应用程序在启动时只对一个大型应用程序可能包含数以百计的JavaScript文件,这似乎并不理想。我遇到的另一个问题是关于AngularJS路由表。我发现的所有示例都包含了所有内容页面的所有路由的硬编码。我不想要一个在路由表中包含数百项的解决方案。

    用RequireJS动态加载JavaScript文件

    对于这个示例单页应用程序,我不想在shell页面中预先加载所有JavaScript文件。这个应用程序可能会增长到数百个内容和JavaScript文件。大型应用程序通常需要数百个JavaScript文件。通常,使用脚本标记逐个加载JavaScript文件。此外,每个文件可能依赖于其他文件。为了动态加载这个示例应用程序的JavaScript文件,我发现了RequireJS JavaScript库。 RequireJS是一个著名的JavaScript模块和文件加载器,它在流行的浏览器的最新版本中得到支持。在RequireJS中,JavaScript代码被分割成模块,每个文件处理一个单一的职责。此外,在加载文件时可能需要配置依赖关系。 RequireJS提供了一种干净的方式来加载和管理你的Javascript应用的依赖。你可以下载RequireJS在http://requirejs.org 或者如果你正在使用Visual Studio,你可以使用Nuget命令: 安装包RequireJS。

    基于约定的路由在AngularJS

    AngularJS自带了一个路由配置,可以根据一个路由路径返回不同的内容页面。我想使用一种基于约定的技术,而不是硬编码所有路由。基本上,我决定给我所有的内容页面命名,并将JavaScript文件与命名约定相关联,这样应用程序就可以解析路由的名称,并动态地确定内容页面需要哪个JavaScript文件。 例如,客户维护内容页面被命名为CustomerMaintenanceController.js,而AngularJS JavaScript控制器文件被命名为CustomerMaintenanceController.js。使用基于约定的方法将允许路由表不受硬编码路由的阻碍。

    遍历示例应用程序

    让我们开始浏览示例应用程序。首先,每个大型应用程序都需要某种身份验证和授权机制来控制对应用程序的访问。这个应用程序将使用一个包含ASP. net的登录页面。NET表单身份验证用于身份验证和授权。一旦经过身份验证,用户就可以访问应用程序的其余部分。由于大型应用程序锁定,他们通常有独立的主网页,一个用于显示和格式化登录page 另一个主页显示应用程序的其余部分通常包含一个头和一个主菜单栏,额外的菜单选项的兼职,一个内容区域,内容页面和页脚区。这个示例应用程序通过拥有多个单页shell页面来支持这一点。成功登录后,用户将被路由到一个新的shell页面。

    多个Shell页面

    第一个shell页面是index.html。这个页面将存放登录和注册用户内容页面。如您所见,只引用了一个JavaScript文件。js将包含RequireJS设置和配置信息,用于动态加载模块、JavaScript文件和这个应用程序的其他依赖项,因为它们是每个单独的内容页面请求所需要的。基于约定的路由技术后,index . html将由AngularJS 控制;控制器indexController.js文件只在用户成功注册或登录时,应用程序将被路由到一个新的层页面称为applicationMasterPage.html 类似于index . html,但将包含一个侧边栏菜单选项。在shell页面中,有一个对ng-view指令的引用。如前所述,这个指令将告诉AngularJS 在shell页面中在何处显示内容页面。 ,,,

    <!-- 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>

    需要的设置和配置文件

    这个应用程序将使用RequireJS进行异步脚本加载和JavaScript依赖管理。正如前面所示,shell页面只有一个对JavaScript文件的引用,即main.js,它位于这个应用程序的根文件夹中。这是RequireJS的配置文件。在下面的JavaScript文件中有三个部分。 第1节定义了加载应用程序所需的通用JavaScript文件和模块所需的所有路径。因为RequireJS只加载JavaScript文件,没有"。实际的JavaScript文件名需要js扩展名。

    第2节定义了垫片节。Shim配置允许RequireJS加载非amd兼容的脚本。异步模块定义(AMD)是一个用于定义模块的JavaScript API,这样模块及其依赖项就可以异步加载。它可以通过绕过同步加载模块和站点内容的其他部分来提高网站的性能。除了在运行时加载多个JavaScript文件之外,AMD还可以在开发期间使用JavaScript文件封装在许多不同的文件中。然后就可以将所有源JavaScript连接并缩小到一个用于生产部署的小文件中。

    第3节通过引用位于scripts文件夹中的application-configuration.js来引导和启动应用程序配置。

    // 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']
    });

    js -引导和配置文件

    AngularJS有两个执行阶段,配置阶段和运行阶段。RequireJS会执行Application-Configuration.js, RequireJS会启动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文件,您会立即看到define函数。define 函数是一个RequireJS 函数加载代码模块只模块不同于一个传统的脚本文件,它定义了一个well-scoped对象,避免污染全局命名空间只可以显式地列出其依赖关系和掌握这些依赖项无需引用全局对象,而是接受依赖as 参数定义了模块的功能。 RequireJS中的模块是模块模式的扩展,其好处是不需要全局变量引用其他模块。RequireJS的模块语法允许模块以尽可能快的速度加载,即使是无序加载,但要按照正确的依赖顺序进行评估。由于没有创建全局变量,因此可以在页面中加载一个模块的多个版本。这个应用程序在应用程序范围内依赖于库angularAMD、angular-route、ui-bootstrap、angular-sanitize和blockUI。

    AngularAMD, UI-Bootstrap, Angular-Sanitize和BlockUI

    js引用angularAMD作为一个依赖项。angularAMD是一个实用工具,它帮助RequireJS在AngularJS应用程序中使用,支持按需加载控制器和第三方模块,如Angular-UI,该应用程序使用。 ,,,, UI-Bootstrap是一个存储库,包含一组基于Bootstrap标记和CSS的本地AngularJS指令。这个应用程序使用了许多来自Angular-UI和Twitter Bootstrap CSS的控件和样式。, 需要angular-sanitize库来允许将HTML注入到视图模板中。默认情况下,AngularJS作为一种安全措施防止HTML标记的注入。最后,这个应用程序使用AngularJS blockUI可配置库,该库允许您阻止用户对AJAX请求的交互。

    动态路由表

    application-configuration.js JavaScript文件的最大目的是设置路由、渲染、和加载JavaScript包含HTML页面和相关控制器只研究如何创建一个动态路由表根据公约没有硬编码的路线是一个冒险。在这次探险中,我发现了Per Ploug的博客:http://scriptogr.am/pploug/post/contonbased-routing-in-angularjs 在他的博客中,他引用了以下可以从AngularJS路由提供者中提取的路由元素: ,,,,,,, /:部分/树/:行动/:id 这个大部分未文档化的功能为实现动态的基于约定的路由打开了大门。 这个示例应用程序的内容页大部分位于名为Views的文件夹中。我为应用程序的每个部分设置了子文件夹,每个子文件夹用于账户、客户、订单、产品等。路径/视图/客户/客户维护映射到客户维护页,订单查询的路径是/视图/订单/订单查询。为了方便控制器的动态加载,我将这些页面的控制器放置在与视图相同的文件夹中。 从而/视图/客户/ CustomerMaintenanceController.js JavaScript文件是客户维护页面的控制器,它有助于简化操作。将相关的代码片段放在项目文件夹结构中可以减少查找代码的麻烦。在MVC环境中,JavaScript文件和控件通常放在单独的文件夹中,当你的项目开始变大时,这会变得很麻烦。

    呈现HTML模板很容易。我只需要设置templateUrl属性如下: “视图/”+ rp。section + '/' + rp。树+ . html。 rp。节和rp。便于执行路由匹配和解析的路由的树引用段。解析路由之后,唯一需要做的就是将.html扩展连接到字符串。

    加载控制器稍微复杂一点。AngularJS路由提供程序的控制器属性只支持静态字符串。它不支持构建字符串,例如: controller = "Views/" + parentPath + "/" + controllerName + " controller "; 需要更多的发现。

    经过几天的研究,我发现我可以应用解析函数来设置控制器属性。通过AngularJS位置服务和RequireJS延迟承诺的组合,我最终能够设置控制器属性,该属性将动态加载内容页的JavaScript控制器文件。JavaScript承诺表示完成一次操作后返回的最终值。

    路由表最终只有两条主路可供AngularJS尝试匹配。第二条路线 /:部分/树/:id 添加以处理随路由传递属性的路由。现在,不管这个应用程序有多大,路由表都将保持较小的大小,并且匹配将只在几个路由上进行,从而提高了路由匹配的性能。

    最后,应用程序configuration.js使用angularAMD来引导AngularJS应用。

    客户维护内容页面-创建和编辑客户

    , Image 2

    单页应用程序的内容页类似于ASP。网络内容页面。对于两者来说,内容页面都是HTML的部分页面。ASP。NET内容页,HTML被注入到ASP中。当内容呈现给浏览器时,通常注入HTML、JavaScript和来自服务器的数据。在单页应用程序中,内容页通过ng-view指令注入到DIV标记中。 ASP。NET内容页带有母版页,所有来自服务器的HTML、JavaScript和数据都呈现给浏览器。在单页面应用程序中,在大多数情况下,最初只有HTML呈现给浏览器。在使用RequireJS的SPA应用程序中,JavaScript将被动态加载。页面所需的任何数据都将在页面加载后通过AJAX调用从服务器提取。

    单页应用程序比ASP直接获得的性能提高之一。NET母版页和内容页是指当从服务器检索每个页面时,单页应用程序内容将在客户机上缓存。使用您喜爱的浏览器的开发人员工具,您可以查看每个页面请求的加载时间,并看到您的内容正在被缓存。最终,所有页面都将被缓存,您最终将通过AJAX请求在internet上拉取服务器数据。所有这些都将带来巨大的响应时间和增强的用户体验。 ,

    <!-- CustomerMaintenance.html -->
    
    <div ng-controller="customerMaintenanceController" ng-init="initializeController()">
    
    <h3> Customer Maintenance  </h3>
         <table class="table" style="100%">
    <tr>
    <td class="input-label" align="right"> <label class="required">Customer Code: </label> </td>
    <td class="input-box">
    <div ng-bind="CustomerCode" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="CustomerCode" type="text" style=" 300px" 
         ng-class="{'validation-error': CustomerCodeInputError}" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label class="required">Company Name: </label> </td>
    <td class="input-box">
    <div ng-bind="CompanyName" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="CompanyName" type="text" style=" 300px" 
                ng-class="{'validation-error': CompanyNameInputError}" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label>Address: </label> </td>
    <td class="input-box">
    <div ng-bind="Address" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="Address" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label>City: </label> </td>
    <td class="input-box">
    <div ng-bind="City" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="City" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label>Region: </label> </td>
    <td class="input-box">
    <div ng-bind="Region" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="Region" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label>Postal Code: </label> </td>
    <td class="input-box">
    <div ng-bind="PostalCode" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="PostalCode" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label>Country: </label> </td>
    <td class="input-box">
    <div ng-bind="CountryCode" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="CountryCode" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label" align="right"> <label>Phone Number: </label> </td>
    <td class="input-box">
    <div ng-bind="PhoneNumber" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="PhoneNumber" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    <tr>
    <td class="input-label-bottom" align="right"> <label>Web Site URL: </label> </td>
    <td class="input-box-bottom">
    <div ng-bind="WebSiteURL" ng-show="DisplayMode"> </div>
    <div ng-show="EditMode"> 
         <input ng-model="WebSiteURL" type="text" style=" 300px" /> 
    </div>
    </td>
    </tr>
    </table>
    
    <span ng-show="ShowCreateButton"> 
    <button class="btn btn-primary btn-large" ng-click="createCustomer()">Create </button> </span>
    <span ng-show="ShowEditButton"> 
    <button class="btn btn-primary btn-large" ng-click="editCustomer()">Edit </button> </span>
    <span ng-show="ShowUpdateButton"> 
    <button class="btn btn-primary btn-large" ng-click="updateCustomer()">Update </button> </span>
    <span ng-show="ShowCancelButton"> 
    <button class="btn btn-primary btn-large" ng-click="cancelChanges()">Cancel </button> </span>
    <div style="padding-top:20px">
    
    <alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)"> 
    <div ng-bind-html="MessageBox"> </div> </alert>
    
    </div>
    </div>

    数据绑定和关注点分离(SoC)

    查看上面示例应用程序的客户维护页面的HTML内容,您可以看到可以创建外观整洁且易于阅读的HTML。内容中没有对JavaScript的引用。 ,,,, AngularJS通过数据绑定指令清晰地分离了内容视图和内容控制器之间的关系。对于输入控件,在客户维护控制器中通过ng-bind和ng-model AngularJS指令和$scope属性实现双向数据绑定。AngularJS中的数据绑定功能与其他JavaScript库(如KnockoutJS)中的数据绑定功能类似。有了数据绑定JavaScript功能,解析浏览器文档对象模型(DOM)的需求就成为过去了——这是一件好事,因为很多JavaScript问题都与解析DOM有关。

    ng-show AngularJS指令使显示和隐藏HTML内容变得很容易。对于客户维护页面,通过设置一个JavaScript AngularJS $scope变量,这将允许页面处于编辑模式或仅显示模式。ng-click AngularjS指令将执行控制器的功能,这些功能将在按钮点击时执行。

    客户维护控制器

    每个控制器在这个示例应用程序将被封装在一个RequireJS define 功能将帮助注册控制器AngularJS又是;此外,定义语句会告诉RequireJS客户维修控制器取决于其他库和服务执行正确又是;在这个例子中,控制器有一个依赖于应用程序配置,customersService和alertsServices功能。这些JavaScript依赖将被RequireJS动态加载。 AngularJS使用依赖注入,所以控制器需要的所有东西都会通过参数注入。如果您希望使用单元测试工具(比如Jasmine)对JavaScript控制器进行单元测试,那么这将非常有用。

    $scope AngularJS对象提供了视图和控制器之间的双向数据绑定。控制器中不会引用对HTML内容的直接引用。控制器通过执行initializeController函数启动,该函数由内容页中的ng-init指令启动。 客户维护页面将引用$routeParams服务,以确定是否传入了客户号。如果是这样,控制器将在customerService上执行一个getCustomer函数,该函数将对服务器进行AJAX调用,服务器将返回JSON格式的客户数据,这些数据将填充$scope属性,这些属性将更新HTML模板。

    当用户按下Create按钮时,控制器将执行createCustomer函数。该函数将创建一个客户属性的JavaScript对象,该对象将被传递到服务器并发布到数据库。这个示例应用程序在服务器端使用Microsoft的Web API、实体框架和SQL-Server,但从技术上讲,您可以使用任何服务器技术与AngularJS前端交互。

    // customerMaintenanceController.js
    
    "use strict";
    define(['application-configuration', 'customersService', 'alertsService'], function (app) 
    {
        app.register.controller('customerMaintenanceController', 
        ['$scope', '$rootScope', '$routeParams', 'customersService', 'alertsService',
    
        function ($scope, $rootScope, $routeParams, customerService, alertsService) 
        {
            $scope.initializeController = function () {
        
                var customerID = ($routeParams.id || "");
               
                $rootScope.alerts = [];
                $scope.CustomerID = customerID;
        
                if (customerID == "") {
                    $scope.CustomerCode = "";
                    $scope.CompanyName = "";
                    $scope.Address = "";
                    $scope.City = "";
                    $scope.Region = "";
                    $scope.PostalCode = "";
                    $scope.CountryCode = "";
                    $scope.PhoneNumber = ""
                    $scope.WebSiteURL = "";
                 
                    $scope.EditMode = true;
                    $scope.DisplayMode = false;
                    $scope.ShowCreateButton = true;
                    $scope.ShowEditButton = false;
                    $scope.ShowCancelButton = false;
                    $scope.ShowUpdateButton = false;
                  
                }
                else
                {
                    var getCustomer = new Object();
                    getCustomer.CustomerID = customerID;
                    customerService.getCustomer(getCustomer, 
                                    $scope.getCustomerCompleted, 
                                    $scope.getCustomerError);
                }
              
            }
    
            $scope.getCustomerCompleted = function (response) {
    
                $scope.EditMode = false;
                $scope.DisplayMode = true;
                $scope.ShowCreateButton = false;
                $scope.ShowEditButton = true;
                $scope.ShowCancelButton = false;
                $scope.ShowUpdateButton = false;
    
                $scope.CustomerCode = response.Customer.CustomerCode;
                $scope.CompanyName = response.Customer.CompanyName;
                $scope.Address = response.Customer.Address;
                $scope.City = response.Customer.City;
                $scope.Region = response.Customer.Region;
                $scope.PostalCode = response.Customer.PostalCode;
                $scope.CountryCode = response.Customer.Country;
                $scope.PhoneNumber = response.Customer.PhoneNumber;
                $scope.WebSiteURL = response.Customer.WebSiteUrl;          
            }
    
            $scope.getCustomerError = function (response) {
                alertsService.RenderErrorMessage(response.ReturnMessage);
            }
            
      
            $scope.createCustomer = function () {          
                var customer = $scope.createCustomerObject();
                customerService.createCustomer(customer, 
                                                $scope.createCustomerCompleted, 
                                                $scope.createCustomerError);
            }
          
            $scope.createCustomerCompleted = function (response, status) {
    
                $scope.EditMode = false;
                $scope.DisplayMode = true;
                $scope.ShowCreateButton = false;
                $scope.ShowEditButton = true;
                $scope.ShowCancelButton = false;
                $scope.CustomerID = response.Customer.CustomerID;
    
                alertsService.RenderSuccessMessage(response.ReturnMessage);
    
                $scope.setOriginalValues();
            }
    
            $scope.createCustomerError = function (response) {
                alertsService.RenderErrorMessage(response.ReturnMessage);
                $scope.clearValidationErrors();
                alertsService.SetValidationErrors($scope, response.ValidationErrors);
            }
        
            $scope.createCustomerObject = function () {
    
                var customer = new Object();
    
                customer.CustomerCode = $scope.CustomerCode;
                customer.CompanyName = $scope.CompanyName;
                customer.Address = $scope.Address;
                customer.City = $scope.City;
                customer.Region = $scope.Region;
                customer.PostalCode = $scope.PostalCode;
                customer.Country = $scope.CountryCode;
                customer.PhoneNumber = $scope.PhoneNumber;
                customer.WebSiteUrl = $scope.WebSiteURL;
    
                return customer;
            }
    
            $scope.clearValidationErrors = function () {
                $scope.CustomerCodeInputError = false;
                $scope.CompanyNameInputError = false;          
            }
          
        }]);
    });

    控制器作为语法

    这个示例应用程序在整个应用程序中使用$scope技术,在视图和控制器之间进行双向数据绑定。在上面的控制器中,您可以看到在整个控制器中使用$scope对象。这一直是AngularJS中执行数据绑定的传统方式。最近,AngularJS控制器进行了一些微妙但强大的改变。 最新的趋势是使用控制器作为ControllerName语法,而不是注入$scope到你的控制器。例如,客户维护控制器可以引用如下视图:

    <div ng-controller="customerController as customer">
    <input ng-model="customer.FirstName" type="text" style=" 300px" />
    <input ng-model="customer.LastName" type="text" style=" 300px" />       
    <div>
    <button class="btn btn-primary btn-large" ng-click="createCustomer()"/>Create</button>
    </div>

    填充数据绑定属性的控制器语法如下:,,,,,,,,,

    this.FirstName = "";
    this.LastName = "";                              

    使用“这”对象引用控制器范围似乎比注入美元范围对象更清洁控制器只re-iterate,美元范围是“经典”技术而“控制器”是一个最近更包容AngularJS只工作很好但是在选择使用哪一个方法,是一致的,选择一个或其他技术又是;有更多的例子使用美元的范围,但“控制器”是加速。一个比另一个好吗?随着时间的推移,角度的变化我们将拭目以待。

    客户服务- AngularJS服务

    AngularJS服务是使用依赖注入(DI)连接在一起的可替换对象。您可以使用服务在应用程序之间组织和共享代码。AngularJS服务是延迟实例化的——只有当应用组件依赖于服务时,AngularJS才实例化服务。 AngularJS服务也是单例的——依赖于一个服务的每个组件都会获得一个对由服务工厂生成的单个实例的引用。AngularJS提供了一些有用的服务(比如$http),但是对于大多数应用程序,你也需要自己创建。

    客户维护控制器依赖于CustomerService。在此应用程序中,客户服务用于组织访问和从应用服务器传递与客户相关的数据所需的所有Web API路由。为了保持示例应用程序的所有控制器不包含控制器中的路由,我为每个部分(客户、订单、产品)创建了服务层。AngularJS服务帮助您组织您的JavaScript,以更好地重用和维护。 客户服务引用控制器设置的回调函数。回调函数在对服务器的调用完成后执行。如您所见,客户服务并不对服务器进行实际的HTTP调用。在define函数中,有一个对将被动态加载的ajaxService的依赖。

    // customerService.js
    
    define(['application-configuration', 'ajaxService'], function (app) {
    
        app.register.service('customersService', ['ajaxService', function (ajaxService) {
    
            this.importCustomers = function (successFunction, errorFunction) {
                ajaxService.AjaxGet("/api/customers/ImportCustomers", 
                    successFunction, errorFunction);
            };
    
            this.getCustomers = function (customer, successFunction, errorFunction) {          
                ajaxService.AjaxGetWithData(customer, "/api/customers/GetCustomers", 
                    successFunction, errorFunction);
            };
    
            this.createCustomer = function (customer, successFunction, errorFunction) {
                ajaxService.AjaxPost(customer, "/api/customers/CreateCustomer", 
                    successFunction, errorFunction);
            };
    
            this.updateCustomer = function (customer, successFunction, errorFunction) {
                ajaxService.AjaxPost(customer, "/api/customers/UpdateCustomer", 
                    successFunction, errorFunction);
            };
         
            this.getCustomer = function (customerID, successFunction, errorFunction) {
                ajaxService.AjaxGetWithData(customerID, "/api/customers/GetCustomer", 
                    successFunction, errorFunction);
            };
    
        }]);
    
    });

    AJAX服务

    为这个应用程序创建的AJAX服务将被所有HTTP请求重用。AJAX服务使用AngularJS $http服务,该服务将对服务器进行实际的http GET和POST调用。服务器调用是RESTful服务,只返回JSON对象。 ,,,, AJAX服务还使用blockUI库在运行HTTP请求时阻止用户与UI交互。此外,还可以提供安全功能来确定用户是否经过身份验证。这个应用程序使用表单身份验证,表单身份验证会随着每个请求向服务器发送身份验证令牌。我添加了一行代码,通过检查来自服务器的响应对象中的自定义isauthenfied属性来检查用户是否仍然通过身份验证。 如果用户的会话已经结束,isauthenated检查将把用户路由到登录页面。将AJAX服务作为管理所有AJAX调用的中心位置,可以方便地实现和更改整个应用程序中所有AJAX调用的功能。

    // ajaxService.js
    
    define(['application-configuration'], function (app) 
    {
        app.register.service('ajaxService', ['$http', 'blockUI', function ($http, blockUI) {
            this.AjaxPost = function (data, route, successFunction, errorFunction) {
                blockUI.start();
                setTimeout(function () {
                    $http.post(route, data).success(function 
                              (response, status, headers, config) 
                    {
                        blockUI.stop();
                        successFunction(response, status);
                    }).error(function (response) {
                        blockUI.stop();
                        if (response.IsAuthenicated == false) 
                        { 
                            window.location = "/index.html"; 
                        }
                        errorFunction(response);
                    });
                }, 1000);
            }
         
            this.AjaxGet = function (route, successFunction, errorFunction) {
                blockUI.start();
                setTimeout(function () {
                    $http({ method: 'GET', url: route }).success(
                    function (response, status, headers, config) {
                        blockUI.stop();
                        successFunction(response, status);
                    }).error(function (response) {
                        blockUI.stop();
                        if (response.IsAuthenicated == false) 
                        { 
                            window.location = "/index.html"; 
                        }
                        errorFunction(response);
                    });
                }, 1000);
            }
    
            this.AjaxGetWithData = function (data, route, successFunction, errorFunction) {
                blockUI.start();
                setTimeout(function () {
                    $http({ method: 'GET', url: route, params: data }).success(
                    function (response, status, headers, config) {
                        blockUI.stop();
                        successFunction(response, status);
                    }).error(function (response) {
                        blockUI.stop();
                        if (response.IsAuthenicated == false) 
                        { 
                            window.location = "/index.html"; 
                        }
                        errorFunction(response);
                    });
                }, 1000);
            }
        
        }]);
    });

    AJAX服务的附加配置

    在application-configuration.js文件中,为AJAX服务器请求添加了附加配置。要配置AngularJS来为每个请求传递表单认证cookie信息,$httpProvider需要为withCredentials属性设置一个值为true。 AngularJS也不通过默认返回传统的“XMLHttpRequest”头在HTTP调用,但你也可以配置$httpProvider服务时配置它。另外,可以将blockuiprovider配置为在执行请求时在UI中显示自定义消息,以及用于阻塞UI的其他配置设置。

    // application-configuration.js
    
    app.config(function ($httpProvider) {
        $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
        $httpProvider.defaults.withCredentials = true;
    });
    app.config(function (blockUIConfigProvider) {
        // Change the default overlay message
        blockUIConfigProvider.message("executing...");
        // Change the default delay to 100ms before the blocking is visible
        blockUIConfigProvider.delay(1);
        // Disable automatically blocking of the user interface
        blockUIConfigProvider.autoBlock(false);
    });

    对每个页面请求进行身份验证

    indexController控制示例应用程序的外壳页面。因此,我在配置阶段在application-configuration.js文件中定义了indexController。这允许indexController在运行阶段之前被加载并在AngularJS中注册。通常大型的基于web的应用程序需要在每个页面请求之前执行身份验证和授权。为了处理这个问题,indexController包含一个在每个页面请求之前检查用户身份验证的函数。 ,,,, AngularJS提供了订阅和侦听客户机上发生的事件的能力。您可以订阅和收听的事件之一是$routeChangeStart事件。此事件发生在每个路由导航请求上。要打开侦听器,只需通过AngularJS $scope订阅事件。美元在声明。

    由于indexController控制了外壳页面,因此将在indexController中订阅$routeChangeStart事件。在下面的示例中,在页面请求之前执行一个HTTP GET请求,以确定用户是否仍然经过身份验证。如果自定义isauthenated属性值为false从响应返回,则用户将被路由到登录页面。此外,还可以提供安全性检查,以查看用户是否具有访问请求的页面路由的权限。

    // indexController.js
    
    var indexController = function ($scope, $rootScope, $http, $location, blockUI) {
                 
        $scope.$on('$routeChangeStart', function (scope, next, current) {                          
            $scope.authenicateUser($location.path(),
                    $scope.authenicateUserComplete, $scope.authenicateUserError);                 
        });
          
        $scope.authenicateUser = function (route, successFunction, errorFunction) {
            var authenication = new Object();
            authenication.route = route;
            $scope.AjaxGet(authenication, "/api/main/AuthenicateUser", 
                    successFunction, errorFunction);
        };
               
        $scope.authenicateUserComplete = function (response) {         
            if (response.IsAuthenicated==false)    
            {           
                window.location = "/index.html";
            }
        }
             
    };

    AngularJS rootScope美元

    在AngularJS中,每个应用程序都有一个根作用域。所有其他作用域都是根作用域的后代作用域。作用域提供了模型和视图之间的分离。您可以填充$rootScope的属性,这些属性将在shell页的生命周期中保留它们的值。一旦用户执行浏览器刷新,$rootScope的值就会丢失,必须重新填充。 ,,,, 这个示例应用程序使用$rootScope保存应用程序最初加载时从服务器返回的菜单选项。用户登录后,服务器将返回一个更扩展的菜单选项列表,以允许用户访问应用程序的其余部分。$rootScope是保存会话级别信息(如菜单选项)的好地方。

    $rootScope.MenuItems = response.MenuItems;       

    在shell页面中,菜单项被数据绑定到一个无序列表,并将为每个页面请求保持填充状态。

    <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>

    为AngularJS AngularUI

    示例应用程序使用了来自AngularUI的各种UI小部件。AngularUI是AngularJS框架的一个伙伴套件。在这个应用程序中使用的主要小部件主要集中在AngularUI工具的UI Bootstrap子集上。UI Bootstrap派生自Twitter Bootstrap,是用AngularJS原生编写的。UI Bootstrap存储库包含一组基于Bootstrap标记和CSS的本地AngularJS目录。因此,不需要依赖jQuery或Bootstrap的JavaScript。

    警报(ui.bootstrap.alert)

    Alert是一个角度版的bootstrap's Alert。该指令可用于从动态模型数据生成警报(使用ng-repeat指令);

    <div style="padding-top:20px">
    <alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">
            <div ng-bind-html="MessageBox"></div>
    </alert>
    </div>

    警告指令允许您呈现红色错误消息、绿色信息消息和黄色警告消息。在客户维护屏幕的这个屏幕截图中,当用户没有输入所需的客户名字段时,会生成一个警告错误消息。我还扩展了警报功能,以突出显示发生错误的输入。

    Image 3 为了进一步扩展警报指令,样例应用程序包括一个自定义警报服务,可以在整个应用程序中重用该服务来呈现警报消息。在业务层验证期间,将在$rootScope上填充消息内容并从服务器组织消息,并在AJAX请求完成后呈现给客户机。

    // alertsService.js
    
    define(['application-configuration'], function (app) 
    {
        app.register.service('alertsService', ['$rootScope', function ($rootScope) {
    
            $rootScope.alerts = [];
            $rootScope.MessageBox = "";
    
            this.SetValidationErrors = function (scope, validationErrors) {
                for (var prop in validationErrors) {
                    var property = prop + "InputError";
                    scope[property] = true;
                }       
            }
    
            this.RenderErrorMessage = function (message) {
                var messageBox = formatMessage(message);
                $rootScope.alerts = [];
                $rootScope.MessageBox = messageBox;
                $rootScope.alerts.push({ 'type': 'danger', 'msg': '' });
            };
    
            this.RenderSuccessMessage = function (message) {
                var messageBox = formatMessage(message);
                $rootScope.alerts = [];
                $rootScope.MessageBox = messageBox;
                $rootScope.alerts.push({ 'type': 'success', 'msg': '' });
            };
    
            this.RenderWarningMessage = function (message) {
                var messageBox = formatMessage(message);
                $rootScope.alerts = [];
                $rootScope.MessageBox = messageBox;
                $rootScope.alerts.push({ 'type': 'warning', 'msg': '' });
            };
    
            this.RenderInformationalMessage = function (message) {
                var messageBox = formatMessage(message);
                $rootScope.alerts = [];
                $rootScope.MessageBox = messageBox;
                $rootScope.alerts.push({ 'type': 'info', 'msg': '' });
            };
    
            this.closeAlert = function (index) {
                $rootScope.alerts.splice(index, 1);
            };
    
            function formatMessage(message) {
                var messageBox = "";
                if (angular.isArray(message) == true) {
                    for (var i = 0; i < message.length; i++) {
                        messageBox = messageBox + message[i];
                    }
                }
                else {
                    messageBox = message;
                }
                return messageBox;
            }
        }]);
    });

    下面的代码片段将在创建新客户记录时出现错误时执行,并演示对警报服务的调用。

    $scope.createCustomerError = function (response) {
        alertsService.RenderErrorMessage(response.ReturnMessage);
        $scope.clearValidationErrors();
        alertsService.SetValidationErrors($scope, response.ValidationErrors);
    }

    Datepicker (ui.bootstrap.datepicker)

    UI Bootstrap Datepicker是一个干净、灵活且完全可定制的日期选择器。用户可以浏览月和年。,,,

    Image 4

    要在输入框上启用Datepicker,只需向输入框添加Datepicker指令,并添加一个带有日历图标的按钮,用户可以单击该按钮来显示Datepicker。

    <tr>
    <tdclass="input-label"align="right"><labelclass="required">Required Ship Date:</label></td>
    <tdclass="input-box"style="height:50px">
    <divng-bind="RequiredDate"ng-show="DisplayMode"></div>
    <divng-show="EditMode">
    <divclass="row">
    <divclass="col-md-6">
    <pclass="input-group">
    
    <inputng-class="{'validation-error': RequiredDateInputError}"type="text"style="100px"datepicker-popup="MM/dd/yyyy"ng-model="RequiredDate"is-open="opened"datepicker-options="dateOptions"date-disabled="disabled(date, mode)"ng-required="true"close-text="Close"/>
    
    <buttontype="button"ng-click="open($event)"><istyle="height:10px"class="glyphicon glyphicon-calendar"></i></button>
    
    </p>
    </div>
    </div>
    </div>
    </td>
    </tr>

    模态(ui.bootstrap.modal

    UI引导程序的模态是一个快速创建基于角度的模态窗口的服务。创建自定义模式很简单,你只需要创建一个部分视图,添加一个控制器并在使用服务时引用它们。 ,

    Image 5

    ,,, 下面的JavaScript代码片段为产品查询模态打开一个HTML模板,并创建该模态的一个实例。当选择一个产品项目时,产品id通过模态实例的result方法返回,该方法从服务器检索产品信息,产品信息返回到父调用页面,模态被取消。

    $scope.openModal = function () {
    
        var modalInstance = $modal.open({
            templateUrl: 'productLookupModal.html',
            controller: ModalInstanceCtrl,
            windowClass: 'app-modal-window'
        });
    
        modalInstance.result.then(function (productID) {
    
            var getProduct = new Object();
            getProduct.ProductID = productID;
            productService.getProduct(getProduct, 
                                      $scope.getProductCompleted, 
                                      $scope.getProductError);
    
        }, function () {
            // function executed on modal dismissal
        });
    };
    
    var ModalInstanceCtrl = function ($scope, $modalInstance) {
    
        $scope.ProductCode = "";
        $scope.ProductDescription = "";
    
        $scope.productSelected = function (productID) {
            $modalInstance.close(productID);
        };
    
        $scope.cancel = function () {
            $modalInstance.dismiss('cancel');
        };
    };

    Typeahead (ui.bootstrap.typeahead)

    Typeahead是Bootstrap v2的Typeahead插件的AngularJS版本。此指令可用于快速创建优雅的typeaheads与任何形式的文本输入。产品查询模式窗口使用Typeahead指令。 ,

    <input type="text" ng-model="Description"
              typeahead="product for products in getProducts($viewValue)">

    上面示例中的typeahead指令将执行getProducts函数,并在输入框中输入每个字母。然后,getProducts函数调用Products服务来执行一个AJAX请求,该请求根据用户输入的值返回一个产品数据页面,并填充产品查询数据网格。

    $scope.getProducts = function () {
        var productInquiry = $scope.createProductInquiryObject();
        productService.getProducts(productInquiry,
                        $scope.productInquiryCompleted, $scope.productInquiryError);
    }

    ,分页(ui.bootstrap.pagination)

    分页是一个轻量级的分页指令,主要用于提供数据网格分页。将负责显示一个分页栏和正确启用/禁用按钮。

    <pagination boundary-links="true" total-items="TotalProducts" 
                    items-per-page="PageSize" ng-change="pageChanged()" 
                    ng-model="CurrentPageNumber" class="pagination-lg" 
                    previous-text="Prev" next-text="Next" first-text="First" 
                    last-text="Last"></pagination>

    这个应用程序的所有数据网格都使用UI引导分页。事实上,在HTML模板和数据绑定功能的帮助下,实现具有分页和排序功能的全功能数据网格并不太难,就像这个应用程序中的网格一样。 ,,, 下面的产品查询数据网格HTML模板演示了如何连接用于排序和分页的视图。控制器中的视图模型中的数据被数据库存储到表体中,行通过ng-repeat AngularJS指令动态呈现。这个指令还用于为每个标题列创建动态标题标签,用户可以单击这些标签对网格进行排序。HTML模板和数据绑定功能为生成动态功能提供了一种强大而干净的方式。在使用HTML模板一段时间后,您可能不想回到ASP模板那样的混乱状态。NET服务器控件生成。

    <!-- productLookupModal.html -->
    
    <table class="table table-striped table-hover" style=" 100%;">
    <thead>
    <tr>
    <th colspan="2" style=" 50%">
    <span ng-bind="TotalProducts"></span> Products
                            </th>
    <th colspan="5" style="text-align: right;  50%">
                                Page <span ng-bind="CurrentPageNumber"></span> of 
    <span ng-bind="TotalPages"></span>
    </th>
    </tr>
    <tr>
    <th ng:repeat="tableHeader in tableHeaders" ng:class="setSortIndicator(tableHeader.label)" 
        ng:click="changeSorting(tableHeader.label)">{{tableHeader.label}}</th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="product in products">
    
        <td style=" 25%; height: 25px"><a ng-click="ok(product.ProductID)" 
                                            style=" cursor pointer; 
                                            text-decoration underline; 
                                            color black">{{product.ProductCode}}</a></td>
    
        <td style=" 50%; white-space: nowrap"><div ng-bind="product.Description"></div></td>
        <td style=" 25%; text-align:left; white-space: nowrap">
                   <div>{{product.UnitPrice | currency}}</diV></td>
    
    </tr>
    
    </tbody>
    </table>
    <pagination boundary-links="true" total-items="TotalProducts" items-per-page="PageSize" 
                ng-change="pageChanged()" ng-model="CurrentPageNumber" class="pagination-lg" 
                previous-text="Prev" next-text="Next" first-text="First" last-text="Last">
    </pagination>

    最后总结一下产品查询网格,下面的产品查询模态控制器包含了一个对定制数据网格服务的引用,该服务是为实现示例应用程序中所有数据网格的排序功能而创建的。这是使用AngularJS服务和工厂将代码封装为干净、易于阅读和维护的可重用小模块的另一个例子。

    // productLookupModalController.js
    
    "use strict";
    define(['application-configuration', 'productsService', 'alertsService', 'dataGridService'], 
        function (app) {
        app.register.controller('productLookupModalController', ['$scope', '$rootScope', 
        'productsService', 'alertsService', 'dataGridService',
    
            function ($scope, $rootScope, productService, alertsService, dataGridService) {
    
                $scope.initializeController = function () {
                             
                    $rootScope.alerts = [];
    
                    dataGridService.initializeTableHeaders();
                    dataGridService.addHeader("Product Code", "ProductCode");
                    dataGridService.addHeader("Product Description", "Description");              
                    dataGridService.addHeader("Unit Price", "UnitPrice");
    
                    $scope.tableHeaders = dataGridService.setTableHeaders();
                    $scope.defaultSort = dataGridService.setDefaultSort("Description");
    
                    $scope.changeSorting = function (column) {
                        dataGridService.changeSorting(
                                        column, $scope.defaultSort, $scope.tableHeaders);
    
                        $scope.defaultSort = dataGridService.getSort();
                        $scope.SortDirection = dataGridService.getSortDirection();
                        $scope.SortExpression = dataGridService.getSortExpression();
                        $scope.CurrentPageNumber = 1;
                        $scope.getProducts();
                    };
    
                    $scope.setSortIndicator = function (column) {
                        return dataGridService.setSortIndicator(column,
                                                                $scope.defaultSort);
    
                    };
    
                    $scope.ProductCode = "";
                    $scope.Description = "";
                    $scope.PageSize = 5;
                    $scope.SortDirection = "ASC";
                    $scope.SortExpression = "Description";
                    $scope.CurrentPageNumber = 1;
                    $rootScope.closeAlert = dataGridService.closeAlert;
                    $scope.products = [];
                    $scope.getProducts();
                }
    
                $scope.productInquiryCompleted = function (response, status) {
                    alertsService.RenderSuccessMessage(response.ReturnMessage);
                    $scope.products = response.Products;
                    $scope.TotalProducts = response.TotalRows;
                    $scope.TotalPages = response.TotalPages;
                }
    
                $scope.searchProducts = function () {
                    $scope.CurrentPageNumber = 1;
                    $scope.getProducts();
                }
    
                $scope.pageChanged = function () {
                    $scope.getProducts();
                }
    
                $scope.getProducts = function () {
                    var productInquiry = $scope.createProductInquiryObject();
                    productService.getProducts(productInquiry(
                                    $scope.productInquiryCompleted,
    				$scope.productInquiryError);
    
                }
    
                $scope.getProductsTypeAheadProductCode = function (productCode) {
                    $scope.ProductCode = productCode;               
                    var productInquiry = $scope.createProductInquiryObject();
    
                    productService.getProductsWithNoBlock(productInquiry, 
                                   		$scope.productInquiryCompleted,
    					$scope.productInquiryError);
    
                }
    
                $scope.getProductsTypeAheadDescription = function (description) {
                    $scope.Description = description;
                    var productInquiry = $scope.createProductInquiryObject();
    
                    productService.getProductsWithNoBlock(productInquiry, 
                                   	$scope.productInquiryCompleted,
    				$scope.productInquiryError);
    
                }
    
                $scope.productInquiryError = function (response, status) {
                    alertsService.RenderErrorMessage(response.Error);
                }
    
                $scope.resetSearchFields = function () {
                    $scope.ProductCode = "";
                    $scope.Description = "";
                    $scope.getProducts();
                }
    
                $scope.createProductInquiryObject = function () {
    
                    var productInquiry = new Object();
    
                    productInquiry.ProductCode = $scope.ProductCode;
                    productInquiry.Description = $scope.Description;
                    productInquiry.CurrentPageNumber = $scope.CurrentPageNumber;
                    productInquiry.SortExpression = $scope.SortExpression;
                    productInquiry.SortDirection = $scope.SortDirection;
                    productInquiry.PageSize = $scope.PageSize;
    
                    return productInquiry;
    
                }
                $scope.setHeaderAlignment = function (label) {
                    if (label == "Unit Price")
                        return { 'textAlign': 'right' }
                    else
                        return { 'textAlign': 'left' }
                }
            }]);
    });

    结论

    我敢说jQuery已经过时了吗?当然,jQuery仍然很流行并得到了广泛的使用,但是最近几年JavaScript框架和实现架构设计模式(如MVC和MVVM (Model-View-ViewModel))的图书馆出现了。在这些框架和库中包括主干。js, embert, js和AngularJS。 ,,, AngularJS是一个MVC/MVVM框架,由谷歌创建,用于构建适当的体系结构和维护的web应用程序。您的应用程序是用可以相互依赖的模块定义的。它通过将带有新属性、标签和表达式的指令附加到页面来增强HTML,以便在HTML中直接定义非常强大的模板。它还将应用程序的行为封装在控制器中,控制器通过依赖注入来实现,这也可以帮助你非常容易地构造和测试你的JavaScript代码。是的,当开发大规模应用程序的前端代码时,你会想要的一切。AngularJS可能会成为继jQuery之后的下一个重要JavaScript产品。 ,,, JavaScript的世界变得非常有趣,我甚至还没有提到MEAN Stack (AngularJS、Express、NodeJS、MongoDB)的激增,它们实现了从前端到后端整个平台的JavaScript。看看这些技术的未来发展应该会非常有趣。

    用于构建示例应用程序的技术

    AngularJS RequireJS Visual Studio Express 2013 for Web Microsoft . net 4.5.1 Microsoft . net c# 微软Web API 2 微软实体框架6.0 SQL Server Express ,

    ,

    本文转载于:http://www.diyabc.com/frontweb/news14828.html

  • 相关阅读:
    数据可视化实例(八): 边缘直方图(matplotlib,pandas)
    数据可视化实例(七): 计数图(matplotlib,pandas)
    数据可视化实例(六): 带线性回归最佳拟合线的散点图(matplotlib,pandas)
    python 并发专题(十四):asyncio (三)实战
    python 并发专题(十三):asyncio (二) 协程中的多任务
    python 并发专题(十三):asyncio (一) 初识
    python 面向对象专题(七):异常处理
    JavaScript手册 | JS Array 对象中的unshift() 方法
    Spring框架:Bean Scopes, 声明一个Bean的范围
    Java 方法
  • 原文地址:https://www.cnblogs.com/Dincat/p/13488446.html
Copyright © 2011-2022 走看看