65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 AngularJS 和 Web API 实现的单页应用程序,并使用 Bootstrap 进行响应式设计。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (22投票s)

2014 年 11 月 21 日

CPOL

4分钟阅读

viewsIcon

89230

downloadIcon

6830

本文介绍了 AngularJS JavaScript 框架,并展示了如何使用它来开发单页应用程序。

目录

什么是 AngularJS?

AngularJS 是一个结构化的 JavaScript 框架,用于构建单页应用程序 (SPA)。AngularJS 是一个完整的客户端框架,提供了几乎所有的服务,如数据绑定、模板、路由、验证、测试、依赖注入和 CRUD 操作的资源等。

AngularJS 使用的设计模式

AngularJS 使用 MVC 设计模式来组织代码。在 MVC 中,数据和代码被分离成三个组件:模型 (Model)、视图 (View) 和控制器 (Controller)。

模型包含应用程序数据。例如

var products = [
    {'name': 'Product 1',
     'description': 'Description of Product 1'},
    {'name': 'Product 2',
     'description': 'Description of Product 2'},
    {'name': 'Product 3',
     'description': 'The 'Description of Product 3'}
  ];

视图包含带有声明式数据绑定的 HTML

<ul>
    <li ng-repeat="product in products">
      <span>{{product.name}}</span>
      <p>{{product.description}}</p>
    </li>
</ul>

控制器用作模型和视图之间的中介。它包含数据模型和事件处理程序。例如

productApp.controller('ProductListCtrl', function ($scope) {
  $scope.price=100;
  $scope.AddProduct= function(product)
  {
    //code goes here
  };
});

Angularjs 中的双向数据绑定

AngularJS 最强大的特性之一是双向数据绑定。在双向数据绑定中,如果用户更改了 UI 值,AngularJS 会自动更新底层数据模型。反之,如果底层数据模型发生更改,Angular 会自动更新 UI。这减少了更新 DOM 的代码编写量。

下图展示了双向数据绑定

路由

通过路由,我们可以将 Index 页划分为多个视图模板,并可以在浏览器中为不同的视图模板添加书签。对于路由,Angular 使用其内置的 "ngRoute" 模块和 $routeProvider 提供程序。示例项目的路由配置如下所示。

var productcatApp = angular.module('productcatApp', [
'ngRoute',
'productcatControllers',
'productcatServices'
]);
productcatApp.config(['$routeProvider',
function ($routeProvider) {
    $routeProvider.
    when('/products', {
        templateUrl: 'Home/ProductList',
        controller: 'ProductListCtrl'
    }).
    when('/products/:productId', {
        templateUrl: 'Home/ProductDetail',
        controller: 'ProductDetailCtrl'
    }).
    when('/productNew', {
        templateUrl: 'Home/ProductNew',
        controller: 'ProductNewCtrl'
    }).
    when('/productUpdate/:productId', {
        templateUrl: 'Home/ProductUpdate',
        controller: 'ProductUpdateCtrl'
    }).
    otherwise({
        redirectTo: '/products'
    });
}]);

对于每个路由,都有一个 Web API URL 用于提取视图模板,以及一个控制器用于将数据绑定到提取的模板。

带说明的示例项目

说明:本示例项目演示了如何从产品列表中显示、添加、编辑和删除产品。

本示例项目的数据库图如下所示。

索引页

Index 页用作主页,其中包含一个容器,该容器始终使用调用的模板进行更新。

<div id="contentWrapper">
    <div class="container">
        <div ng-view></div>
    </div>
</div>

当用户路由到新 URL 时,带有 Angular 指令 ng-viewdiv 的内容将被新的 HTML 模板替换。

示例项目执行以下步骤。

步骤 1:显示产品列表

下面显示了显示产品列表的 HTML 模板。文件 ProductList.cshtml 包含服务器上的此模板。

<div class="row">
    <div class="col-xs-6 col-sm-6 col-md-6 col-lg-6">
        <button class="btn btn-primary btn-lg btn-block" ng-click="newProduct()">New</button>
    </div>
</div>
<div class="row" ng-repeat="product in products">
    <div class="col-xs-6 col-sm-8 col-md-8 col-lg-8">
        <a href="#/products/{{product.id}}">{{product.name}}</a>
    </div>
    <div class="hidden-xs col-sm-1 col-md-1 col-lg-1">
        <a href="#/products/{{product.id}}"><img class="img-thumbnail img-responsive" 
          ng-src="{{product.imageUrl}}"/></a>
    </div>
    <div class="col-xs-3 col-sm-2 col-md-2 col-lg-2">
        <a class="btn btn-large btn-success" href="#/productUpdate/{{product.id}}">Update</a>
    </div>
    <div class="col-xs-3 col-sm-2 col-md-2 col-lg-2">
        <button class="btn btn-large btn-success" 
                ng-click="deleteProduct(product)">Delete</button>
    </div>
</div>

在上述模板中,使用 ng-repeat="product in products" 来循环 products 列表,使用 {{product.name}} 语法进行双向数据绑定。并且使用 class="col-xs-6 col-sm-6 col-md-6 col-lg-6" 语法用于 Bootstrap。

调用此模板的路由部分是

when('/products', {
    templateUrl: 'Home/ProductList',
    controller: 'ProductListCtrl'
})

当请求 URL https:///#/products 时,Angular 会向 Web API URL 'Home/ProductList' 发送 Ajax 请求以获取新的 HTML 模板,返回后 Angular 将控制器 'ProductListCtrl' 与新模板进行数据绑定。

此模板的控制器是

var productcatControllers = angular.module('productcatControllers', []);
productcatControllers.controller('ProductListCtrl', ['$scope', '$routeParams', '$location', 
                                 '$route', 'productService',
function ($scope, $routeParams, $location, $route, productService) {
    productService.query(function (data) {
        $scope.products = data;
    });
    $scope.deleteProduct = function (product) {
        debugger;
        productService.delete({ id: product.id }, function () {
            $route.reload();
        });
    }
    $scope.newProduct = function () {
        $location.path('/productNew');
    }
}]);

这里,Angular Resource 模块用于从服务器提取数据并从服务器删除产品。这里,newProduct() 函数用于重定向到新产品添加页面。

Resource 模块的配置是

var phonecatServices = angular.module('productcatServices', ['ngResource']);
phonecatServices.factory("productService", function ($resource) {
    return $resource(
        "/api/Product/:id",
        { id: "@id" },
        {
            "update": { method: "PUT" }

        }
    );
});

上述语句创建了一个名为 productService 的服务,并将 $resource 服务作为参数传递,该服务用于数据库操作。

服务器端 Web API 代码是

public HttpResponseMessage GetProducts()
        {
            var collection = db.Products.Select(x => new
            {
                id = x.id,
                name = x.name,
                imageUrl = x.imageUrl
            });
            var yourJson = new JavaScriptSerializer().Serialize(collection);
            var response = this.Request.CreateResponse(HttpStatusCode.OK);
            response.Content = new StringContent(yourJson, Encoding.UTF8, "application/json");
            return response;
        }

步骤 2:添加新产品

服务器上的 ProductNew.cshtml 页面包含此 UI 的 HTML 模板。

调用此模板的路由部分是

when('/productNew', {
    templateUrl: 'Home/ProductNew',
    controller: 'ProductNewCtrl'
})

此模板的控制器是

productcatControllers.controller('ProductNewCtrl', ['$scope', '$routeParams', '$location', 
                     'productService',
function ($scope, $routeParams, $location, productService) {
    $scope.product = { name: "", imageUrl: "", ProductDetail: { number: 0, price: 0.0, 
                       description: "", companyName: "" } };
    $scope.addNewProduct = function (product) {
        productService.save(product, function () {
            $location.path('/products');
        });
    }
    $scope.cancelNewProduct = function () {
        $location.path('/products');
    }
}]);

在上面的控制器中,使用 productService 服务的 save 函数将产品保存在数据库中,保存后会重定向到产品列表页面。

服务器端 Web API 代码是

public HttpResponseMessage PostProduct(Product product)
        {
            if (ModelState.IsValid)
            {
                db.Products.Add(product);
                db.ProductDetails.Add(product.ProductDetail);
                db.SaveChanges();
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
        }

步骤 3:编辑产品

服务器上的 ProductUpdate.cshtml 页面包含此 UI 的 HTML 模板。

调用此模板的路由部分是

when('/productUpdate/:productId', {
    templateUrl: 'Home/ProductUpdate',
    controller: 'ProductUpdateCtrl'
})

此模板的控制器是

productcatControllers.controller('ProductUpdateCtrl', ['$scope', '$routeParams', 
                                 '$location', 'productService',
function ($scope, $routeParams, $location, productService) {
    productService.get({ id: $routeParams.productId }, function (data) {
        $scope.product = data;
    });
    $scope.updateProduct = function (product) {
        var post = productService.get({}, { id: product.id }, function (data) {
            debugger;
            post.name = product.name;
            post.imageUrl = product.imageUrl;
            post.ProductDetail.number = product.ProductDetail.number;
            post.ProductDetail.price = product.ProductDetail.price;
            post.ProductDetail.description = product.ProductDetail.description;
            post.ProductDetail.companyName = product.ProductDetail.companyName;
            productService.update(post, function () {
                $location.path('/products');
            });
        });
    }
    $scope.cancelUpdateProduct = function () {
        $location.path('/products');
    }
}]);

在上面的控制器中,使用 productService 服务的 update 函数更新数据库中的 product,更新后会重定向到产品列表页面。

服务器端 Web API 代码是

public HttpResponseMessage PutProduct(int id, Product product)
        {
            if (!ModelState.IsValid)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }
            if (id != product.id)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest);
            }
            Product productEntity = 
                    db.Products.Where(p => p.id == product.id).FirstOrDefault();
            productEntity.name = product.name;
            productEntity.imageUrl = product.imageUrl;
            productEntity.ProductDetail.number = product.ProductDetail.number;
            productEntity.ProductDetail.price = product.ProductDetail.price;
            productEntity.ProductDetail.description=product.ProductDetail.description;
            productEntity.ProductDetail.companyName = product.ProductDetail.companyName;
            db.Entry(productEntity).State = EntityState.Modified;
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }
            return Request.CreateResponse(HttpStatusCode.OK);
        }

步骤 4:显示产品详细信息

服务器上的 ProductDetail.cshtml 页面包含此 UI 的 HTML 模板。

调用此模板的路由部分是

when('/products/:productId', {
    templateUrl: 'Home/ProductDetail',
    controller: 'ProductDetailCtrl'
})

此模板的 Controller

productcatControllers.controller('ProductDetailCtrl', ['$scope', '$routeParams', 
                                 '$location', 'productService',
function ($scope, $routeParams, $location, productService) {
    productService.get({ id: $routeParams.productId }, function (data) {
        $scope.product = data;
    });
    $scope.backToProduct = function () {
        $location.path('/products');
    }
}]);

在上面的控制器中,使用 productService 服务的 get 函数通过 id 从数据库提取产品,并将数据分配给产品 Model

服务器端 Web API 代码是

public HttpResponseMessage GetProduct(int id)
{
    var product = db.Products.Include("ProductDetail").Where(p => p.id == id).Select(x => new
    {
        id = x.id,
        name = x.name,
        imageUrl = x.imageUrl,
        ProductDetail = new { id = x.ProductDetail.id, number = x.ProductDetail.number, 
                        price = x.ProductDetail.price, 
                        description = x.ProductDetail.description,
                        companyName = x.ProductDetail.companyName }
    }).FirstOrDefault();
    var json = new JavaScriptSerializer().Serialize(product);
    var response = this.Request.CreateResponse(HttpStatusCode.OK);
    response.Content = new StringContent(json, Encoding.UTF8, "application/json");
    return response;
}

结论

总而言之,如今 Angular 是一个流行的 JavaScript 框架,用于开发单页应用程序。请下载附件的 zip 文件以获取示例项目。运行它需要 VS2012 和 SQL Server 2008。

历史

  • 2014年11月21日:初始版本
© . All rights reserved.