• [译]用AngularJS构建大型ASP.NET单页应用(三)


    原文地址:http://www.codeproject.com/Articles/808213/Developing-a-Large-Scale-Application-with-a-Single

    AngularUI

    下面的示例中使用了AngularUI的各种UI组件。AngularUI 是AngularJS 框架的一个辅助套件。示例中使用的主要组件大部分来在AngularUI 的一个子集UI Bootstrap。UI Bootstrap是从Twitter Bootstrap派生出来的,它使用AngularJS编码实现。 UI Bootstrap库包含了一套使用Bootstrap标识和样式的AngularJS 指令。 这使得它不依赖jQuery.js和Bootstrap.js。

    Alert (ui.bootstrap.alert)

    AngularJS Alert 是由Bootstrap 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>

    为了更好的拓展警告指令, 这个示例应用程序包含了一个自定义的指令服务(custom alerts service).它可以在整个应用程序中使用,以渲染警告信息.信息的内容设置在$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控件 是一种清洁、灵活和完全可定制的日期选择器。

    把Datepicker相关的参数添加到输入框,然后添加一个按钮,用户可以通过单击日历图标显示Datepicker。

    <tr>
    <td class="input-label" align="right"><label class="required">Required Ship Date:</label></td>
    <td class="input-box" style="height:50px">
    <div ng-bind="RequiredDate" ng-show="DisplayMode"></div>
    <div ng-show="EditMode">
    <div class="row">
    <div class="col-md-6">
    <p class="input-group">
    
    <input ng-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" />
    
    <button type="button" ng-click="open($event)"><i style="height:10px"
                  class="glyphicon glyphicon-calendar"></i></button>
    
    </p>
    </div>
    </div>
    </div>
    </td>
    </tr>

    Modal (ui.bootstrap.modal)

    UI Bootstrap的Modal是一种服务,它可以快速的创建拥有Angular属性的模态对话框.创建定制化的modal是很简单,只需创建部分视图,增加一个控制器,然后在使用服务的时候引用它们.

    下面的JavaScript代码为Product Inquiry Modal打开了一个HTML模板,并创建了一个modal实例.当一个产品项目被选中的时候,产品id通过modal实例的结果方法返回.这个modal实例从服务器获取产品信息.产品信息返回到调用页面后,modal消失.

    $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是AngularJS Bootstrap v2版本的typeahead插件.这个指令可以快速创建一个漂亮的基于任意文本框的typeahead控件.Product Inquiry Modal窗口使用了Typeahead指令

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

    在上面例子中的typeahead指令,将把输入框中的输入信息作为参数并执行getProducts函数.然后getProducts函数会调用Products Service来执行一个AJAX请求.这个请求将返回一个基于用户输入信息的产品数据的页面,并设置产品查询数据列表.

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

    Pagination (ui.bootstrap.pagination)

    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 Bootstrap分页控件.实际上,有了HTML模板和数据绑定功能,实现多用途的数据列表是很容易的.这个数据列表包含类似于这个应用程序的分页和排序功能.

    下面的产品查询数据列表的HTML模板,详细描述了如何使用视图来排序以及分页.在控制器的视图模型中的数据是和表格绑定,其中表格的行是通过AngularJS的ng-repeat指令动态渲染的.这个指令也用于动态创建表头.用户可以通过点击表头来排序.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 Services和Factories的例子.它把代码封装成小的可重复使用的,简洁的,易读的和易于维护的模块.

    // 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仍然很流行并广泛使用.但是,过去的一些年见证了结构化设计模式的框架和库,如MVC和MVVM(Model-View-ViewModel)的崛起.这些框架和库包括Backbone.js, Ember.js和AngularJS等.

    AngularJS是一个MVC/MVVM framework.它由google创建,以开发具有良好体系结构的和可维护的web应用程序.AngularJS定义了大量的概念来合理的组织web应用程序.应用程序由相互依赖的模块来定义.它通过新的属性或者标签和表达式,关联指令到页面来增强HTML,以定义功能强大的模板.它也将应用程序的行为封装到控制器,这些控制器通过依赖注入的方式实例化.这有利于结构化,而且非常容易测试JavaScript代码.是的,这里有你开发大型应用程序前端代码所需的所有东西.AngularJS可能是自jQuery之后,下一个JavaScript大事件. 

    创建示例应用程序所用到的技术

    AngularJS
    RequireJS
    Visual Studio Express 2013 for Web
    Microsoft .NET 4.5.1
    Microsoft .NET C#
    Microsoft Web API 2
    Microsoft Entity Framework 6.0
    SQL Server Express

    完~~~

  • 相关阅读:
    Scrum Meeting 6 -2014.11.12
    Scrum Meeting 5 -2014.11.11
    Bing词典vs有道词典比对测试报告——体验篇之成长性及用户控制权
    团队项目的用户需求及反馈
    Scrum Meeting 4 -2014.11.8
    Scrum Meeting 3 -2014.11.5
    bing词典vs有道词典对比测试报告——功能篇之细节与用户体验
    Bing词典vs有道词典比对测试报告——功能篇之辅助功能,差异化功能及软件的效能
    Bing词典vs有道词典比对测试报告
    hdu 5087 次长升序串的长度
  • 原文地址:https://www.cnblogs.com/lazio10000/p/4153928.html
Copyright © 2020-2023  润新知