使用 AngularJS 开发一个大规模的单页应用(SPA)(5)

Modal (ui.bootstrap.modal

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

使用 AngularJS 开发一个大规模的单页应用(SPA)


下面的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"
                previous-text="Prev" next-text="Next" first-text="First"
                last-text="Last"></pagination>

这个应用程序的所有的数据列表都使用了UI Bootstrap分页控件.实际上,有了HTML模板和数据绑定功能,实现多用途的数据列表是很容易的.这个数据列表包含类似于这个应用程序的分页和排序功能.

下面的产品查询数据列表的HTML模板,详细描述了如何使用视图来排序以及划分分页.在控制器的视图模型中的数据是和表格绑定,其中表格的行是通过AngularJS的ng-repeat指令动态渲染的.这个指令也用于为每个列头创建动态的列头标签.用户可以通过点击列头来排序.HTML模板和数据绑定功能提供了强大的和简洁的动态生成功能.使用一段时间的HTML模板后,你将不愿再回到使用ASP.NET Server Control的一团糟的状况了.

<!-- productLookupModal.html -->
 
<table>
<thead>
<tr>
<th colspan="2">
<span ng-bind="TotalProducts"></span> Products
                        </th>
<th colspan="5">
                            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><a ng-click="ok(product.ProductID)"
                                       >{{product.ProductCode}}</a></td>
 
    <td><div ng-bind="product.Description"></div></td>
    <td>
              <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"
            previous-text="Prev" next-text="Next" first-text="First" last-text="Last">
</pagination>

最后,包装一下产品查询列表,下面的产品查询模态控制器包含了一个自定义数据列表服务引用.它用来在示例应用程序中,为所有的数据列表实现排序功能.这是又一个使用AngularJS Services和Factories的例子.它把代码封装成小的可重复使用的,简洁的,易读的和易于维护的模块.

// productLookupModalController.js
 
"use strict";
define([&apos;application-configuration&apos;, &apos;productsService&apos;, &apos;alertsService&apos;, &apos;dataGridService&apos;],
    function (app) {
    app.register.controller(&apos;productLookupModalController&apos;, [&apos;$scope&apos;, &apos;$rootScope&apos;,
    &apos;productsService&apos;, &apos;alertsService&apos;, &apos;dataGridService&apos;,
 
        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 { &apos;textAlign&apos;: &apos;right&apos; }
                else
                    return { &apos;textAlign&apos;: &apos;left&apos; }
            }
        }]);
});

结论

我敢说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大事件.
JavaScript世界开始变得很有意思.我还没有提到MEAN Stack(AngularJS,Express,NodeJS,MongoDB)的繁荣.它实现了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

AngularJS 的详细介绍请点这里
AngularJS 的下载地址请点这里

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/41d2bc23261fec21a26cca1c517ed6fc.html