zoukankan      html  css  js  c++  java
  • 7.1SportsStore:Navigation and Checkout

    1. 准备示例项目
    2. 使用真实的产品数据

    现在,要切换到使用真实的数据,从Deployd服务器获取。

    AngularJS通过一个叫做$http的服务,为Ajax请求提供支持。作者将在第三部分详细讲解它是怎么工作的,在第23章讲解$http服务。现在有个简单的认识,修改顶级控制器sportsStoreCtrl:

    angular.module("sportsStore")

    .constant("dataUrl", "http://localhost:5500/products")

    .controller("sportsStoreCtrl", function ($scope, $http, dataUrl) {

    $scope.data = {};

    $http.get(dataUrl)

    .success(function (data) {

    $scope.data.products = data;

    })

    .error(function (error) {

    $scope.data.error = error;

    });

    });

    大多数JavaScript方法的调用,包含AngularJS组件里的方法,synchronous,意味着在当前任务完成前,不会执行下个声明。在web应用中,这就不能工作,因为我们想让用户在请求在后台执行,用户可以继续和应用交互。

    作者使用Ajax请求获取需要的数据。Ajax基于Asynchronous JavaScript and XML,重要的是单词asynchronous。一个Ajax请求是一个标准的HTTP请求以异步的方式发生,换句话说,在后台。AngularJS使用promises请求异步操作,这和你使用像jQeury这样的库很像(作者在第5章介绍,并在第20章解释)。

    $http服务定义了不同的方法,来做不同种类的Ajax请求。Get方法,是作者这里使用的,使用HTTP GET方法,请求参数中的URL。作者已经将URL定义为一个叫做dataUrl的常量,并使用第6章中测试过的Deployd服务器的URL。

    $http.get方法,从Ajax请求开始,应用继续运行,直到请求已经完毕。当服务器已经相应请求,AngularJS需要一种方法,来提醒用户,这就是promise的由来。该$http.get方法返回一个对象,定义success和error方法。作者传递函数给这些方法,AngularJS promises调用这些函数中的一个,告诉作者,请求是什么结果。

    如果HTTP请求的一切都运行很好,AngularJS会调用作者传递给success方法的函数,会自动转换JSON数据为Javascript对象,将他们作为参数传递给函数。如果Ajax HTTP请求过程中有任何问题,AngularJS会调用作者传递给error方法的函数。

    提示:JSON基于JavaScript Object Notation,是一种数据交换格式,广泛地应用于web应用。

    在success函数中,AngularJS会自动执行JSON数据的转换。作者只需将从服务器获取的数据,指派给控制器scope的data.products变量。Error函数将对象指派给scope的data.error变量,来表述问题。

    2.1理解scope

    当应用第一次启动,HTML内容被生成和显示给用户,尽管没有产品信息可用。

    在一个点以后,内容渲染完毕,数据从服务器回来,指派给scope中的data.products变量。这时,AngularJS更新所有的绑定和计算依赖于产品数据的行为的结果,确保新数据传播到应用的每一处。本质上,AngularJS scope是 live 数据存储,响应和传播改变。你会看到在本书中看到不少这样的例子。

    2.2、处理Ajax错误

    处理成功的Ajax请求很简单,因为只需将数据指派个scope,让AngularJS更新视图中所有的绑定和指令。作者不得不有些艰苦地处理错误,添加一些新元素到视图,用于出现问题是显示出来。在下面可以看到

    <!DOCTYPE html>

    <html ng-app="sportsStore">

    <head>

    <title>SportsStore</title>

    <script src="angular.js"></script>

    <link href="bootstrap.css" rel="stylesheet" />

    <link href="bootstrap-theme.css" rel="stylesheet" />

    <script>

    angular.module("sportsStore", ["customFilters"]);

    </script>

    <script src="controllers/sportsStore.js"></script>

    <script src="filters/customFilters.js"></script>

    <script src="controllers/productListControllers.js"></script>

    </head>

    <body ng-controller="sportsStoreCtrl">

    <div class="navbar navbar-inverse">

    <a class="navbar-brand" href="#">SPORTS STORE</a>

    </div>

    <div class="alert alert-danger" ng-show="data.error">

    Error ({{data.error.status}}). The product data was not loaded.

    <a href="/app.html" class="alert-link">Click here to try again</a>

    </div>

    <div class="panel panel-default row" ng-controller="productListCtrl"

    ng-hide="data.error">

    <div class="col-xs-3">

    <a ng-click="selectCategory()"

    class="btn btn-block btn-default btn-lg">Home</a>

    <a ng-repeat="item in data.products | orderBy:'category' | unique:'category'"

    ng-click="selectCategory(item)" class=" btn btn-block btn-default btn-lg"

    ng-class="getCategoryClass(item)">

    {{item}}

    </a>

    </div>

    <div class="col-xs-8">

    <div class="well"

    ng-repeat=

    "item in data.products | filter:categoryFilterFn | range:selectedPage:pageSize">

    <h3>

    <strong>{{item.name}}</strong>

    <span class="pull-right label label-primary">

    {{item.price | currency}}

    </span>

    </h3>

    <span class="lead">{{item.description}}</span>

    </div>

    <div class="pull-right btn-group">

    <a ng-repeat=

    "page in data.products | filter:categoryFilterFn | pageCount:pageSize"

    ng-click="selectPage($index + 1)" class="btn btn-default"

    ng-class="getPageClass($index + 1)">

    {{$index + 1}}

    </a>

    </div>

    </div>

    </div>

    </body>

    </html>

    作者给视图添加了一个新的div元素,用于显示错误给用户。使用ng-show指令,当表达式计算结果为true时,显示。

    提示:作者将在第10章描述ng-show和ng-directives。

    传递给error函数的对象,定义了status和message属性。状态属性设为HTTP error code,message属性返回一个字符串,描述问题。作者将status属性包含在message中显示给用户。

    1. 创建部分视图

    App.html文件的HTML变得复杂了,以后添加新特性会很糟糕。

    幸运的是,作者可以分离文件,使用ng-include指令,在运行时来导入这些文件。创建view/productList.html文件:

    <div class="panel panel-default row" ng-controller="productListCtrl"

    ng-hide="data.error">

    <div class="col-xs-3">

    <a ng-click="selectCategory()"

    class="btn btn-block btn-default btn-lg">Home</a>

    <a ng-repeat="item in data.products | orderBy:'category' | unique:'category'"

    ng-click="selectCategory(item)" class=" btn btn-block btn-default btn-lg"

    ng-class="getCategoryClass(item)">

    {{item}}

    </a>

    </div>

    <div class="col-xs-8">

    <div class="well"

    ng-repeat=

    "item in data.products | filter:categoryFilterFn | range:selectedPage:pageSize">

    <h3>

    <strong>{{item.name}}</strong>

    <span class="pull-right label label-primary">

    {{item.price | currency}}

    </span>

    </h3>

    <span class="lead">{{item.description}}</span>

    </div>

    <div class="pull-right btn-group">

    <a ng-repeat=

    "page in data.products | filter:categoryFilterFn | pageCount:pageSize"

    ng-click="selectPage($index + 1)" class="btn btn-default"

    ng-class="getPageClass($index + 1)">

    {{$index + 1}}

    </a>

    </div>

    </div>

    </div>

    作者将产品和分类列表的定义元素,拷贝到HTML文件。部分视图,是HTML的片段,意味着不需要html,head,body元素。

    <!DOCTYPE html>

    <html ng-app="sportsStore">

    <head>

    <title>SportsStore</title>

    <script src="angular.js"></script>

    <link href="bootstrap.css" rel="stylesheet" />

    <link href="bootstrap-theme.css" rel="stylesheet" />

    <script>

    angular.module("sportsStore", ["customFilters"]);

    </script>

    <script src="controllers/sportsStore.js"></script>

    <script src="filters/customFilters.js"></script>

    <script src="controllers/productListControllers.js"></script>

    </head>

    <body ng-controller="sportsStoreCtrl">

    <div class="navbar navbar-inverse">

    <a class="navbar-brand" href="#">SPORTS STORE</a>

    </div>

    <div class="alert alert-danger" ng-show="data.error">

    Error ({{data.error.status}}). The product data was not loaded.

    <a href="/app.html" class="alert-link">Click here to try again</a>

    </div>

    <ng-include src="'views/productList.html'"></ng-include>

    </body>

    </html>

    提示:使用部分视图有三个益处。第一个是打破应用,使其更加好管理。第二,创建的HTML片段,可以用于应用中反复重用。第三,使得为用户显示不同功能区域更容易。作者会在Defining URL Routes 这一节再说。

    指令的创建器,可以指明它被如何应用:作为元素,作为属性,作为class,甚至作为HTML 评论。作者将在第16章解释。当AngularJS遇到ng-include指令,它做一个Ajax请求,加载src属性指定的文件,在该元素位置,插入内容。展现给用户的内容看起来没有什么不同,但已经简单地装扮了app.html文件,将product列表相关的HTML都分离到一个文件中。

    提示:当使用ng-include指令,在单引号中,指定文件的名字。如果没有这么做,那么指令会查找一个scope属性,来获取文件的名字。

    1. 创建购物车

    用户可以看到可用的产品,但没有购物车,作者不能销售任何东西。

    要实现购物车特性,许多部分需要变化,包括要创建一个自定义的AngularJS组件。

    4.1、定义购物车模块和服务

    到此为止,都是通过文件的类型组织文件:过滤器包含在过滤器文件夹,视图包含在视图文件夹等等。但总是有一些功能,是自包含的,但需要混合AngularJS组件。你可以继续用component类型组织文件,但作者找到了更有用的方法,基于他们集体扮演的功能,来组织文件,放在components文件夹下。购物车功能适用于这种组织类型。作者需要不分时图和几个组装件,来得到想要的效果。从创建components/cart文件夹开始,添加一个新的JavaScript文件叫做cart.js:

    angular.module("cart", [])

    .factory("cart", function () {

    var cartData = [];

    return {

    addProduct: function (id, name, price) {

    var addedToExistingItem = false;

    for (var i = 0; i < cartData.length; i++) {

    if (cartData[i].id == id) {

    cartData[i].count++;

    addedToExistingItem = true;

    break;

    }

    }

    if (!addedToExistingItem) {

    cartData.push({

    count: 1, id: id, price: price, name: name

    });

    }

    },

    removeProduct: function (id) {

    for (var i = 0; i < cartData.length; i++) {

    if (cartData[i].id == id) {

    cartData.splice(i, 1);

    break;

    }

    }

    },

    getProducts: function () {

    return cartData;

    }

    }

    });

    作者在叫做cart的新模块上穿件一个自定义服务。AngularJS为服务提供一些功能,(只有一个对象会被创建,并被所有基于该服务的组件共享)。

    并不是只有使用一个服务,才能为我们演示AngularJS的重要特性,但以这种方式实现购物车会很好,因为有一个共享的实例,确保任何组件能访问购物车,用户的产品选择有相同的视图。

    作者将会在第18章解释,基于你要尝试的实现,这里有不同的方式创建服务。这里使用Module.factory方法,传入服务的名字(本例中是cart),和一个factory函数。当AngularJS需要服务时,该factory函数会被调用,创建服务对象,用以相应。因为一个服务对象用于整个应用,factory函数只被调用一次。

    作者的cart服务factory函数,返回一个对象,它有三个方法,操作一个没有通过服务直接曝露的数据数组,这是作者用于演示的,你不必暴露服务的所有工作。该cart对象定义三个方法,介绍如下。作者在购物车中将产品表现为对象,定义了id,name,price属性,来描述产品,和一个count属性,记录用户添加入篮子的数量。

    Method

    Description

    addProduct(id,name,price)

    添加指定产品到购物车,如果产品已经存在,增加该产品的需求数量

    removeProduct(id)

    使用指定id,移除产品

    getProducts()

    返回购物车中的对象数组

    4.2、创建购物车部件

    下一步,要创建一个部件,总结购物车的内容,提供给用户,意味着开始结账流程。下面,作者要创建一个自定义directive。Directives,是子包含,可重用的功能的单元,它位于AngularJS开发的核心位置。如果你用AngularJS,你会使用许多内建指令。单位了方便,你会找到你自己创建的自定义命令,来让功能适合你的应用。

    使用指令,你能做很多。它甚至支持jQuery的删减版,叫jqLite,来操作DOM元素。简单来讲,命令允许你写任何东西,从简单的helpers,到复杂的特性,并决定结果编织成当前的应用,或在其他应用中完整地重用他们。下面是作者给cart.js文件中,创建widget directive:

    angular.module("cart", [])

    .factory("cart", function () {

    var cartData = [];

    return {

    // ...service statements omitted for brevity...

    }

    })

    .directive("cartSummary", function (cart) {

    return {

    restrict: "E",

    templateUrl: "components/cart/cartSummary.html",

    controller: function ($scope) {

    var cartData = cart.getProducts();

    $scope.total = function () {

    var total = 0;

    for (var i = 0; i < cartData.length; i++) {

    total += (cartData[i].price * cartData[i].count);

    }

    return total;

    }

    $scope.itemCount = function () {

    var total = 0;

    for (var i = 0; i < cartData.length; i++) {

    total += cartData[i].count;

    }

    return total;

    }

    }

    };

    });

    通过在AngularJS模块上调用directive方法,创建指令,并传递指令的名字(本例中是cartSummary),和一个factory函数。该函数返回一个directive definition object。该指令定义对象定义了属性,会告诉AngularJS,你的质量做什么,怎样做。当作者定义cartSumary指令时,定义了三个属性,下面简短地介绍他们。

    Name

    Description

    Restrict

    指定指令如何应用。作者使用E的值,意味着该指令只能作为一个元素应用。更公共的值是EA,意味着指令能作为一个元素或一个属性应用。

    TemplateUrl

    指定哪个URL的部分视图的内容,要插入到指令的元素
    controller 制定一个控制器,给部分试图提供数据和行为

    简而言之,作者的指令定义了一个控制器,告诉AngularJS,使用components/cart/cartSummary.html视图,并限制指令,只能作为一个元素应用。

    <style>
    .navbar-right { float: right !important; margin-right: 5px;}
    .navbar-text { margin-right: 10px; }
    </style>
    <div class="navbar-right">
    <div class="navbar-text">
    <b>Your cart:</b>
    {{itemCount()}} item(s),
    {{total() | currency}}
    </div>
    <a class="btn btn-default navbar-btn">Checkout</a>
    </div>

    提示:分布试图包含一个style元素,重定义Bootstrap CSS的导航条的样式。作者不总是喜欢将style元素放在视图中,但他这么做了,因为这些样式只影响该试图,并且只是少量CSS。在所有其他情况下,作者更希望定义一个单独的CSS文件,导入到应用的主HTML文件中。

    该部分视图使用控制器的行为,来显示items的数量,和items的合计值。这里也有一个元素,叫Checkout,点击该按钮,此刻还没有任何反应。

    4.2.1、应用购物车部件

    把购物车部件应用到应用,需要三个步骤:添加script元素,倒入JavaScript文件的内容,添加对cart模块的依赖,并添加指令元素到markup。

    <!DOCTYPE html>
    <html ng-app="sportsStore">
    <head>
    <title>SportsStore</title>
    <script src="angular.js"></script>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="bootstrap-theme.css" rel="stylesheet" />
    <script>
    angular.module("sportsStore", ["customFilters", "cart"]);
    </script>
    <script src="controllers/sportsStore.js"></script>
    <script src="filters/customFilters.js"></script>
    <script src="controllers/productListControllers.js"></script>
    <script src="components/cart/cart.js"></script>
    </head>
    <body ng-controller="sportsStoreCtrl">
    <div class="navbar navbar-inverse">
    <a class="navbar-brand" href="#">SPORTS STORE</a>
    <cart-summary />
    </div>
    <div class="alert alert-danger" ng-show="data.error">
    Error ({{data.error.status}}). The product data was not loaded.
    <a href="/app.html" class="alert-link">Click here to try again</a>
    </div>
    <ng-include src="'views/productList.html'"></ng-include>
    </body>
    </html>

    注意作者使用cartSummary,定义指令,添加到app.html文件的元素,是cart-summary。AngularJS normalizes 组建,命名这些格式之间的映射,作者会在第15章解释。

    4.3、添加产品选择按钮

    作为所有AngularJS开发,这里有许多预付的付出,来开发功能和其他特性,但是这些东西,如cart,都能在其他应用中重用。作者下一步是给产品明细添加按钮,让用户可以把产品添加到购物车。第一,他需要给控制器添加一个行为,让产品列表视图可以操作购物车。下面是controllers/productListController.js文件。

    angular.module("sportsStore")
    .constant("productListActiveClass", "btn-primary")
    .constant("productListPageCount", 3)
    .controller("productListCtrl", function ($scope, $filter,
    productListActiveClass, productListPageCount, cart) {
    var selectedCategory = null;
    $scope.selectedPage = 1;
    $scope.pageSize = productListPageCount;
    $scope.selectCategory = function (newCategory) {
    selectedCategory = newCategory;
    $scope.selectedPage = 1;
    }
    $scope.selectPage = function (newPage) {
    $scope.selectedPage = newPage;
    }
    $scope.categoryFilterFn = function (product) {
    return selectedCategory == null ||
    product.category == selectedCategory;
    }
    
    $scope.getCategoryClass = function (category) {
    return selectedCategory == category ? productListActiveClass : "";
    }
    $scope.getPageClass = function (page) {
    return $scope.selectedPage == page ? productListActiveClass : "";
    }
    $scope.addProductToCart = function (product) {
    cart.addProduct(product.id, product.name, product.price);
    }
    });

    作者在cart服务上声明了一个依赖,定义了一个叫做addProductToCart的行为,它持有一个产品对象,使用它调用cart服务的addProduct方法。

    提示:在服务上声明一个依赖,然后通过scope选择性地暴露它的功能,这种模式,在AngularJS开发中很常见。视图可以通过scope访问可用的行为,但是作者在第六章演示了,当控制器是嵌套的,或者当指令被定义,scopes可以继承另一个scope。

    作者然后再产品明细的部分视图上,添加按钮元素,调用addProductToCart行为。

    <div class="panel panel-default row" ng-controller="productListCtrl"
    ng-hide="data.error">
    <div class="col-xs-3">
    <a ng-click="selectCategory()"
    class="btn btn-block btn-default btn-lg">Home</a>
    <a ng-repeat="item in data.products | orderBy:'category' | unique:'category'"
    ng-click="selectCategory(item)" class=" btn btn-block btn-default btn-lg"
    ng-class="getCategoryClass(item)">
    {{item}}
    </a>
    </div>
    <div class="col-xs-8">
    <div class="well"
    ng-repeat=
    "item in data.products | filter:categoryFilterFn | range:selectedPage:pageSize">
    <h3>
    <strong>{{item.name}}</strong>
    <span class="pull-right label label-primary">
    {{item.price | currency}}
    </span>
    </h3>
    <button ng-click="addProductToCart(item)"
    class="btn btn-success pull-right">
    Add to cart
    </button>
    <span class="lead">{{item.description}}</span>
    </div>
    <div class="pull-right btn-group">
    <a ng-repeat=
    "page in data.products | filter:categoryFilterFn | pageCount:pageSize"
    ng-click="selectPage($index + 1)" class="btn btn-default"
    ng-class="getPageClass($index + 1)">
    {{$index + 1}}
    </a>
    </div>
    </div>
    </div>

    提示:Bootstrap让作者可以将a和button元素显示成相同的样子,为了方便,他倾向于交替地使用它们。当使用URL routing时,a元素更有用。

    5、添加URL导航

    路由系统,可以让不同部分试图,自动显示。这使得构建大型应用更加容易。开始,作者需要创建一个视图,当用户开始结账流程时,显示。放在views/checkoutSummary.html文件中。

    <div class="lead">
    This is the checkout summary view
    </div>
    <a href="#/products" class="btn btn-primary">Back</a>

    5.1、定义URL路由

    作者要定义一个routes,在制定URL和要显示的视图之间做映射。首先做两个映射,/product和/chekcout URLs,到产品列表页productList.html和checkoutSummary.html视图。

    <!DOCTYPE html>
    <html ng-app="sportsStore">
    <head>
    <title>SportsStore</title>
    <script src="angular.js"></script>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="bootstrap-theme.css" rel="stylesheet" />
    <script>
    angular.module("sportsStore", ["customFilters", "cart", "ngRoute"])
    .config(function ($routeProvider) {
    $routeProvider.when("/checkout", {
    templateUrl: "/views/checkoutSummary.html"
    });
    $routeProvider.when("/products", {
    templateUrl: "/views/productList.html"
    });
    $routeProvider.otherwise({
    templateUrl: "/views/productList.html"
    });
    });
    </script>
    <script src="controllers/sportsStore.js"></script>
    <script src="filters/customFilters.js"></script>
    <script src="controllers/productListControllers.js"></script>
    <script src="components/cart/cart.js"></script>
    <script src="ngmodules/angular-route.js"></script>
    </head>
    <body ng-controller="sportsStoreCtrl">
    <div class="navbar navbar-inverse">
    <a class="navbar-brand" href="#">SPORTS STORE</a>
    <cart-summary />
    </div>
    <div class="alert alert-danger" ng-show="data.error">
    Error ({{data.error.status}}). The product data was not loaded.
    <a href="/app.html" class="alert-link">Click here to try again</a>
    </div>
    <ng-view />
    </body>
    </html>

    作者将angular-route.js文件,导入到应用。该文件提供的功能,在一个ngRoute的模块中,作者将它声明为sportsStore模块的依赖。

    要设置作者的路由,他在模块对象上调用config方法。该方法将一个函数作为他的参数,当模块加载后,单应用还没被执行前,执行该函数。它提供为任何一次性地配置任务,提供机会。

    作者传递给config方法的函数,在一个provider上声明依赖。作者之前提到,有不同的方法可以创建AngularJS服务,一种就是通过配置provider对象来创建。作者在该provider上声明的一个依赖,是$routeProvider,用于在应用中设置URL路由。

    提示:作者将在第18章,解释如何使用providers创建服务,在第22章,解释如何使用$route服务和$routeProvider。

    这这使用$routeProvider对象定义的两个模块,设置他需要的路由。when方法允许作者将URL匹配到视图,象这样:

    $routeProvider.when("/checkout", {
    templateUrl: "/views/checkoutSummary.html"
    });

    该声明告诉AngularJS,当URL是/checkout时,他想用/views/checkoutSummary.html文件

    显示。同样,也制定了一个所有when方法都不匹配URL时,使用/views/ProductList.html视图文件,来相应。这听起来象定义了一个fallback路由。

    http://localhost:5000/app.html#/checkout

    AngularJS不会监视整个URL,因为象http://localhost:5000/checkout 这样的路由,会导致浏览器跳过AngularJS应用,尝试从服务器加载一个不同的文档。

    URL Effect
    http://localhost:5000/app.html#/checkout  Displays the checkoutSummary.htmlview
    http://localhost:5000/app.html#/products  Displays the productList.htmlview
    http://localhost:5000/app.html#/other Displays the productList.htmlview (because of the
    fallback route defined by the otherwisemethod)
    http://localhost:5000/app.html  Displays the productList.htmlview (because of the
    fallback route defined by the otherwisemethod)

    提示:作者在第22章回讲到,你可以使用HTML5 History API,启用支持,改变URL的监视,使得象http://localhost:5000/checkout 这样的URL也能工作。当心,因为浏览器实现不同,当用户手工修改URL时,会导致浏览器尝试加载一个不同的文档。

    5.1.1、显示路由视图

    路由策略,定义了给定URL路径,要显示的视图,但没有告诉AngularJS,将他们加载到哪里。作者需要使用ng-view指令,它在ngRoute模块中定义。使用ng-view替换ng-include。

    <body ng-controller="sportsStoreCtrl">
    <div class="navbar navbar-inverse">
    <a class="navbar-brand" href="#">SPORTS STORE</a>
    <cart-summary />
    </div>
    <div class="alert alert-danger" ng-show="data.error">
    Error ({{data.error.status}}). The product data was not loaded.
    <a href="/app.html" class="alert-link">Click here to try again</a>
    </div>
    <ng-view />
    </body>

    这里不需要配置选项和设置。只需要添加指令,告诉AngularJS,将当前选择的视图的内容,插入到哪里。

    5.2、使用URL路由来导航

    已经定义了作者的URL路由,并应用ng-view指令,作者可以改变URL路径,来导航应用。首先改变Checkout按钮。

    <style>
    .navbar-right { float: right !important; margin-right: 5px;}
    .navbar-text { margin-right: 10px; }
    </style>
    <div class="navbar-right">
    <div class="navbar-text">
    <b>Your cart:</b>
    {{itemCount()}} item(s),
    {{total() | currency}}
    </div>
    <a href="#/checkout" class="btn btn-default navbar-btn">Checkout</a>
    </div>

    作者给a元素添加了一个href属性,点击该元素,会导致浏览器导航到新URL(它是定位到已经加载过的文档)。该导航变化,会被AngularJS路由服务发现,导致ng-view指令,显示checkoutSummary.html视图。

    注意点击checkout后,地址由http://localhost:5000/app.html 变为http://localhost:5000/app.html#/checkout,通过点击返回键,可以回退到上个视图。

    使用URL路由的特点是,该组建可以在没有任何预备知识的时候,通过ng-view指令来改变布局。

    6、开始结账流程

    创建一个叫做cartSummaryController,

    angular.module("sportsStore")
    .controller("cartSummaryController", function($scope, cart) {
    $scope.cartData = cart.getProducts();
    $scope.total = function () {
    var total = 0;
    for (var i = 0; i < $scope.cartData.length; i++) {
    total += ($scope.cartData[i].price * $scope.cartData[i].count);
    }
    return total;
    }
    $scope.remove = function (id) {
    cart.removeProduct(id);
    }
    });

    将新控制器添加到sportsStore模块,并依赖于cart服务。它通过一个叫做cartData的scope属性,曝露内容,定义了一个行为,计算购物车中产品的总价值,和从购物车中移除产品。

    <h2>Your cart</h2>
    <div ng-controller="cartSummaryController">
    <div class="alert alert-warning" ng-show="cartData.length == 0">
    There are no products in your shopping cart.
    <a href="#/products" class="alert-link">Click here to return to the catalogue</a>
    </div>
    <div ng-hide="cartData.length == 0">
    <table class="table">
    <thead>
    <tr>
    <th>Quantity</th>
    <th>Item</th>
    <th class="text-right">Price</th>
    <th class="text-right">Subtotal</th>
    </tr>
    </thead>
    <tbody>
    <tr ng-repeat="item in cartData">
    <td class="text-center">{{item.count}}</td>
    <td class="text-left">{{item.name}}</td>
    <td class="text-right">{{item.price | currency}}</td>
    <td class="text-right">{{ (item.price * item.count) | currency}}</td>
    <td>
    <button ng-click="remove(item.id)"
    class="btn btn-sm btn-warning">Remove</button>
    </td>
    </tr>
    </tbody>
    <tfoot>
    <tr>
    <td colspan="3" class="text-right">Total:</td>
    <td class="text-right">
    {{total() | currency}}
    </td>
    </tr>
    </tfoot>
    </table>
    <div class="text-center">
    <a class="btn btn-primary" href="#/products">Continue shopping</a>
    <a class="btn btn-primary" href="#/placeorder">Place order now</a>
    </div>
    </div>
    </div>

    这里没有新技术。该控制器使用ng-controller指令来制定,当购物车中没有产品时,或有产品时,用ng-show和ng-hide指令来显示一个警告。

    6.1、应用结账总结

    下一步是添加一个script元素到app.html文件,并定义附加routes,用于完成结账流程。

    <!DOCTYPE html>
    <html ng-app="sportsStore">
    <head>
    <title>SportsStore</title>
    <script src="angular.js"></script>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="bootstrap-theme.css" rel="stylesheet" />
    <script>
    angular.module("sportsStore", ["customFilters", "cart", "ngRoute"])
    .config(function ($routeProvider) {
    $routeProvider.when("/complete", {
    templateUrl: "/views/thankYou.html"
    });
    $routeProvider.when("/placeorder", {
    templateUrl: "/views/placeOrder.html"
    });
    $routeProvider.when("/checkout", {
    templateUrl: "/views/checkoutSummary.html"
    });
    $routeProvider.when("/products", {
    templateUrl: "/views/productList.html"
    });
    $routeProvider.otherwise({
    templateUrl: "/views/productList.html"
    });
    });
    </script>
    <script src="controllers/sportsStore.js"></script>
    <script src="filters/customFilters.js"></script>
    <script src="controllers/productListControllers.js"></script>
    <script src="components/cart/cart.js"></script>
    <script src="ngmodules/angular-route.js"></script>
    <script src="controllers/checkoutControllers.js"></script>
    </head>
    <body ng-controller="sportsStoreCtrl">
    <div class="navbar navbar-inverse">
    <a class="navbar-brand" href="#">SPORTS STORE</a>
    <cart-summary />
    </div>
    <div class="alert alert-danger" ng-show="data.error">
    Error ({{data.error.status}}). The product data was not loaded.
    <a href="/app.html" class="alert-link">Click here to try again</a>
    </div>
    <ng-view />
    </body>
    </html>
  • 相关阅读:
    nginx详解
    keeplived高可用集群
    mysql主从同步
    elasticsearch基础
    redis集群管理--sentinel
    socket阻塞与非阻塞,同步与异步,select,pool,epool
    django+channels+dephne实现websockrt部署
    Django+Nginx+uWSGI生产环境部署
    进制转换
    对golang指针的理解
  • 原文地址:https://www.cnblogs.com/msdynax/p/3786335.html
Copyright © 2011-2022 走看看