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

?
? ?

?

4.2.1、应用购物车部件

4.3、添加产品选择按钮

  1. 添加URL导航

5.1、定义URL路由

5.1.1、显示路由视图

5.2、使用URL路由来导航

  1. 开始结账流程

6.1、应用结账总结

7.1SportsStore:Navigation and Checkout,布布扣,bubuko.com

时间: 2024-10-07 15:08:53

7.1SportsStore:Navigation and Checkout的相关文章

JSF -&gt; 导航(Navigation)

在使用jsf框架时,肯定会用到faces-config.xml. 而其中就会出现很多的Navigation项. 其实这些Navigation就是一些页面跳转的东西. 以下内容来自http://blog.sina.com.cn/s/blog_600046120100to0e.html 导航(Navigation) 现在对jsf中的导航进行些小结,分为三部分来说,参考与core jsf 1,静态的导航 2,动态的导航 3,高级的导航 主要讨论在你web程序中如何配置导航,即如何让你的程序从一个页面跳

git 常用命令 mv rm checkout revert reset

关于上节讲的git add 时需要添加注释信息,也可以在git commit时再添加 [email protected] MINGW64 /c/laoni/PycharmProjects/github_test (master) $ git add UI.js laoni@DESKTOP-TPPLHIB MINGW64 /c/laoni/PycharmProjects/github_test (master) $ git commit -m "添加UI.js" [master 358c

ROS机器人程序设计(原书第2版)补充资料 (捌) 第八章 导航功能包集入门 navigation

ROS机器人程序设计(原书第2版)补充资料 (捌) 第八章 导航功能包集入门 navigation 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中使用. 本章三个非常重要概念:TF,SLAM,AMCL.务必掌握. 补充内容:http://blog.csdn.net/zhangrelay/article/details/50299417 第216页: 简介本章要点. 第217页: 导航综合功能包组成架构等. 补充如下: 目录 配置并使用导航功能

代码回滚:git reset、git checkout和git revert区别和联系

git reset.git checkout和git revert是你的Git工具箱中最有用的一些命令.它们都用来撤销代码仓库中的某些更改,而前两个命令不仅可以作用于提交,还可以作用于特定文件. 因为它们非常相似,所以我们经常会搞混,不知道什么场景下该用哪个命令.在这篇文章中,我们会比较git reset.git checkout和git revert最常见的用法.希望你在看完后能游刃有余地使用这些命令来管理你的仓库. Git仓库有三个主要组成——工作目录,缓存区和提交历史.这张图有助于理解每个

git checkout 命令详解(转)

在日常的git操作中,git checkout——检出,是我们的常用命令.最为常用的两种情形是创建分支和切换分支. 在下面的命令中,使用了一些简写,在这里说明一下: git st # git status git ci # git commit git br # git branch git co # git checkout git mg # git merge git line # git log --oneline 当然,你也可以直接在git中敲命令,将这些简写添加到git配置中 git

POJ 1984 Navigation Nightmare 二维带权并查集

题目来源:POJ 1984 Navigation Nightmare 题意:给你一颗树 k次询问 求2点之间的曼哈顿距离 并且要在只有开始k条边的情况下 思路:按照方向 我是以左上角为根 左上角为原点 dx[i]为i点距离根的x坐标 dy[]是y坐标 这两个可以通过路径压缩求出 只不过是二维而已 #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; const int m

UINavigationController出现nested push animation can result in corrupted navigation bar的错误提示

今天在测试过程中,出现了这样一个bug,分别有两种情景: (前提是:app是基于UINavigationController构建的) 1.从Controller-A中push进来B.在B中点击返回,返回的界面为黑色一片.再做返回操作就crash了. 如图1: 2.从Controller-A中push进入B,此时B中tableview出现错位现象(图2),tableview被navigationbar覆盖了一部分,在B中再push一个C进来.此时只显示了C的navigationbar,但下方的vi

IOS 改变Navigation的返回按钮

两个办法: 1, 手动为每一个UIViewController添加navigationItem的leftButton的设置代码 2,为UINavigationController实现delegate,在pop和push的时候改变当前和上一页的navigationItem.title 以下是封装的一些基础方法,供参考: + (void) navigationItem:(UINavigationItem*)navigationItem setTitle:(NSString*)title; + (vo

jenkins中Check-out Strategy的各选项测试

Use'svn update' as much as possible 第一次发布的时候,会把工作目录下的所有文件清空,然后check-out一份完整的项目到工作目录下: 以后更新的时候,不会判断已有文件是否在svn里存在.比如工作目录下的文件123在svn里不存在,那么更新的时候不会删除123. 不会判断工作目录下的文件是否被改动,只会判断svn是否有新版本需要更新.比如工作目录下的文件zzz.txt内容为zzz,svn上的zzz.txt内容为空,如果svn上zzz.txt没有新版本,则在更新