Bootstrap6.1:SportStore:一个真真的利用

前面的小例子让我们演示了AngularJS的局部风味,但她俩少上下文。要解决这个题材,作者要创建一个简短不过真实的电子商务应用。

笔者用开创一个在线产品分类,客户可经分类及页面浏览,一个购物车用户增长或移除产品,当客户在结账时,进入他们的购物明细,列有她们之订单。作者为会见创造一个管制区域,包含CRUD,管理分类,并且保护它们,只有已报到的组织者才能够改改其。

作者本章的对象,通过创办一个重复实在的例证,是被您发到,一个实的AngularJS开发。作者想关注于AngularJS,当然,也使简明地同外部系统融为一体,如数据存储,会了忽视任何的,如开发处理。

提拔:单元测试

AngularJS对单元测试提供成千上万杰出的支持,但笔者不克以本书的末尾一节才说她。因为您真的要明白AngularJS如何行事,才会写起综合的单元测试。

作者以第25章节才云单元测试,但是他建议我们按梯次读,这样才能够领悟单元测试的特征,是如何构建的。

  1. 开始

此间而安装有而选取的AngularJS特性,来装服务器,让他分发数据。

  1. 预备数据

先是步是创建一个新的Deployd应用。你而创建一个门道,来放生成的公文。作者称它路径为deployd,放在angularjs文件夹和级别。

只顾:作者在首先回为您生载Deployd,如果你未曾那么开,快去下充斥吧。

在指令行中,进入新路径,输入下面代码

dpd create sportsstore

如若开新服务器,输入下面的命

dpd –p 5500 sportsstore\app.dpd

dashboard 

提拔:这是windows风格的公文分隔符。你在其他平台上或许是sportsstore/app.dpd。

在Deployd dashboard,用于配制服务器,显示在浏览器中。

  1. 缔造数据结构

产一致步是很快Deployd,要存储的数目的组织。在dashboard上点击绿色的充分按钮,从pop-up菜单中,选择Collection。设置集合的讳吧
“/products”(没有双引号)。

Deployd会提示而穿件JSON对象的性,属性如下:

Name

Type

Required

name

String

Yes

description

String

Yes

category

String

Yes

price

Number

Yes

当你成功了性添加,dashboard会匹配,确保您输入的性能名字不错,并摘了正确的门类。

提示:注意Deployd已经添加id属性。他见面用于独一无二地标识数据库中的对象。Deployd会自动指派独一无二之价为id属性。在第8章节,当作者实现管理员功能时,会依据这些价值。

  1. 添加数码

而今,作者就定义了靶的组织。作者可以增长产品的周密。点击Data
link,在左边。他会显grid编辑器,让您填入属性的值。

绝不担心着id属性的价,因为Deployd会自动生成他们。

唤醒:Deployd的number字段的浮点显示。

  1. 测试数据服务

倘若测试Deployd已经不错配置并工作,打开浏览器窗体,导航及脚的URL:

http://localhost:5500/products

若是你曾于当地电脑安装Deployd,并没改其的端口号。/products
URL是查询/products集合中之情节,作为一个JSON字符串标示。注意,id字段不同。

  1. 未雨绸缪以

当笔者开始勾画以前,作者用准备angularjs文件夹,来创造路径结构,放置AngularJS和Bootstrap文件。

1.2.1、创建路径结构

以angularjs文件夹下,创建如下目录:

Name

Description

components

包含自包含的客制化AngularJS组件

controllers

包含应用的控制器,作者会在第13章讲。

filters

包含自定义filters。作者在第14章深度介绍filters

ngmodules

包含可选的AngularJS模块。

views

包含SportsStore应用的局部视图。Views包含指令和filters的组合。作者在第10-17章讲。

 

1.2.2、安装AngularJS和BootStrap文件

作者的习惯,将AngularJS JavaScript文件以及Bootstrap
CSS文件直接坐angularjs路径,将AngularJS可选模块放在ngmodules路径。

Angularjs文件夹下产生:angular.js,bootstrap.css,bootstrap-theme.css

Ngmodules文件夹下起:angular-route.js,angular-resource.js

 

1.2.3、构建核心的草

笔者喜欢先用静态数据模拟各个组成部分的始末,开始一个新的AngularJS应用。SportsStroe应用之主干布局,是经典的片排布局。第一排是分类,用户过滤第二排列要展示的出品组。

先是步是如果开创一个一品HTML文件。在angularjs文件夹下,新建app.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", []);

</script>

</head>

<body>

<div class="navbar navbar-inverse">

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

</div>

<div class="panel panel-default row">

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

Categories go here

</div>

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

Products go here

</div>

</div>

</body>

</html>

欠文件包含两单AngularJS指定,第一只凡是调用angular.module方法。

<script>

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

</script> 

模块是AngularJS应用的头等构建块,该法调用穿件一个新的模块,叫做sportsStroe。

第二单方面,是html元素上之ng-app指令:

<html ng-app="sportsStore"> 

Ng-app指令使得sportsStore模块中定义之效能,在HTML范围外可用。作者喜欢将ng-app指令下至html元素上,你吧足以在body元素上。

提示:用http://localhost:5000/app.html
。来聘该页面。而无是Deployd服务器的5500端口。

  1. 展示模拟产品数量

笔者先定义本地模拟初始化数据,第7节会用自Deployd服务器取得的数据替换该数据。

2.1、创建控制器

先是使加以一个控制器。该控制器创建后,会呢一体应用服务,作者成为世界级控制器。然后,作者会以几单有关的控制器,放到一个文件被,但拿一流控制器放在app.html文件被。

提拔:作者保持一流控制器和另外文件分割的原委,是它们在版本控制系统有变更时,要一致目就是能够看出她。当主功能逐步成就,顶级控制器会发生逐渐发生变化,这是地下地打破,几乎在所有的行使中。这点于开发周期中,作者想只要知道顶级控制器后面的组成部分事物,可以保反经过了充分的测试。

在controllers/sportsStore.js :

angular.module("sportsStore")

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

$scope.data = {

products: [

{ name: "Product #1", description: "A product",

category: "Category #1", price: 100 },

{ name: "Product #2", description: "A product",

category: "Category #1", price: 110 },

{ name: "Product #3", description: "A product",

category: "Category #2", price: 210 },

{ name: "Product #4", description: "A product",

category: "Category #3", price: 202 }]

};

});

小心第一履行调用angular.module方法,这在app.html也出一致之方法调用。不同之处是,在app.html中定义之module,作者提供了一个附加的参数,像这么:

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

次只参数是一个勤组,它本凡是拖欠的,它是sportsStore模块基于的模块列表,它高速AngularJS,定位并提供这些模块中含的功力。作者稍后会补加元素到是数组中。但是今,重要之是,要掌握,当你提供一个数组——空或非空,它还高速AngularJS,要创一个初的模块。当你尝试创建一个模块,但是其早已有时时,AngularJS会报告一个荒谬,所以若待保证您的模块名字的唯一性。

用作比,在sportsStroe.js文件被调用angular.module方法,不分包第二只参数:

angular.module("sportsStore")

忽视第二只参数,会告知AngularJS,你一旦稳住一个已定义了之模块,在这种情况下,如果指定的模块不存,AngularJS会报告一个荒唐,
所以你如管模块已被创造。

使用angular.module方法返回的Module对象,都能够让用于定义应用效益。作者就运用controller方法,定义了一个控制器。

顾:作者不常像这么以HTML文件中,创建主应用模块,因为他好吃概括地放到Javascript文件被。作者以宣示分成多有些有的因,是累利用angular.module方法,导致无终止的眼花缭乱,作者想让你注意到她。

SportsStore应用中,顶级控制器的要害角色,是概念用于在不同视图上显得的数据。在第13节,会看出多单控制器级联排列。

留意:当作者定义控制器的范围达到之多少常常,将数据对象的高频组,定义在data上,它见面叠加到scope。你必以定义数据时好小心,因为要您在scope上直接着属性(如$scope.products=[data]),因其他控制器可以读取,但连续不能够修改数据。作者用当第13章详细说明。

2.2、显示产品明细

一经显得产品明细,需要被app.html文件添加一些HTML。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", []);

</script>

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

</head>

<body ng-controller="sportsStoreCtrl">

<div class="navbar navbar-inverse">

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

</div>

<div class="panel panel-default row">

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

Categories go here

</div>

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

<div class="well" ng-repeat="item in data.products">

<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>

</div>

</body>

</html>

此地发出三只转移。第一只,添加了一个script元素,导入sportsStore.js文件。该文件包含sportsStoreCtrl控制器。因为作者就于app.html文件被定义了sportsStore模块,然后在sportsStore.js文件被一贯并使她,作者用确保sportsStore模块的定义script,出现于援前。

仲单改变,是故ng-controller指令,将控制器采用至视图,像这么:

<body ng-controller="sportsStoreCtrl">

作者会动用sportsStoreCtrl控制器,为周应用提供支撑,所以,他拿它使用至body元素上。

2.2.1、生成内容元素

最终一个变动,是开创了成品明细元素。AngularJS提供的一个不过灵之命令,是ng-repeat,它吧数据数组中的每个对象生成元素。

<div class="well" ng-repeat="item in data.products">

下一场作者以数绑定表达式中,引用当前目标。

<div class="well" ng-repeat="item in data.products">

<h3>

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

<span class="pull-right label label-primary">{{item.price | currency}}</span>

</h3>

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

</div>

Name和description值被直接插入HTML元素,但price属性不是这么:作者以了一个filter。一个filter格式或order,是一旦在view中显示的数据值。AngularJS有部分内建的filters,包含currency
filter,它用币种金额,格式化数字。Filters使用 | 符号应用。Item.price |
currency,会告知 AngularJS,使用currency
filter,传递item对象的price属性的值。

Currency
filter默认使用USD格式,但作者会以第14章说明,怎么下AngularJS本地化filters,来展示任何币种格式。作者吧会见当第14回,说明外建造filters,并被你来得怎么创造和谐之。

  1. 著分类列表

生一样步是展示分类列表,让用户可过滤显示的活组。实现该特性,需要变更客户用于导航的素,选择一个活分类后,更新明细面板,只展示为入选分类的产品。

3.1、创建分类列表

作者想打活数据对象动态变化分类元素,而休是硬编码HTML元素。动态途径设置起来再复杂一点,但她会容许SportsStore应用能够活动感应产品分类的更动。这意味着作者不得不打成品数据对象中变化一个举世无双之归类名的列表。该特性AngularJS不分包,但由此创设与运一个自定义过滤器,能够简单地促成。在filters路径下,创建一个customFilters.js:

angular.module("customFilters", [])

.filter("unique", function () {

return function (data, propertyName) {

if (angular.isArray(data) && angular.isString(propertyName)) {

var results = [];

var keys = {};

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

var val = data[i][propertyName];

if (angular.isUndefined(keys[val])) {

keys[val] = true;

results.push(val);

}

}

return results;

} else {

return data;

}

}

});

从今定义过滤器,使用Module对象的filter方法定义,它经过angular.module方法获得并创造。作者选择新建一个模块,叫做customFilters,来含有他的过滤器,主要可以来得什么定义并以运中做多单模块。

唤醒:当你吃现存模块添加组件或创一个新模块时,没有先后顺序。作者想他定义的功能以今后的不等应用里用时,他赞同于创造模块。自定义过滤器倾向于可选用,因为数量格式是任何AngularJS应用都待之,也是开发者需要的公格式。

Filter方法的参数,是filter的讳,例子中凡是unique,它回到一个factory
function,该函数并无实际工作,而是返回一个filter
function。当AngularJS需要创造一个filter的实例时,它调用factory
function,filter function就见面吃调用,以实施过滤。

具的filter
function都有一个参数,传递他们得之格式的数码,但作者的filter定义了一个格外的参数,叫做propertyName,它会用来指定要于如深成哪个字段的绝无仅有列表。过滤效果的贯彻深粗略:枚举数据对象的始末,用propertyName参数,构建一个旷世之值的列表。

提示:作者没有没有发生以过滤器功能硬编码为寻category属性,这会限制unique过滤器在另外以之录取。

过滤效果返回过滤后的数目。作者以anagular.isArray和angular.isString,检查传入的数额是否是反复组,属性名是否是字符串。然后,他用angular.isUndefined方法,检查该属性是否定义。AngularJS提供平等组有因此底工具方法,包含允许而检查对象与性类型。作者会于第5段完整地叙述。如果过滤器接收至一个数组和一个属于性名,然后他转移并回一个独一无二的属于性值的数组。不然,他回去将收到的数额原封不动地回来。

提示:让过滤器仅显示用户需要之情节,而不是当scope中修改原始数据。

3.2、生成分类导航链接

下一样步,生成用户可点击的出品分类的领航链接。这要利用上节创的unique过滤器。

<!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>

</head>

<body ng-controller="sportsStoreCtrl">

<div class="navbar navbar-inverse">

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

</div>

<div class="panel panel-default row">

<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">

{{item}}

</a>

</div>

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

<div class="well" ng-repeat="item in data.products">

<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>

</div>

</body>

</html>

在sportsStore模块中,定义一个针对customFilters的依。

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

当即就是是declaring a
dependency。在本例中,作者声明sportsStore模块依赖让customFilters模块的效力。这会促成AngularJS定位customFilters模块,并保管她可用,然后可以引用它含有的零部件,如过滤器和控制器。该过程叫resolving
the dependency。

提拔:声明与治本模块和外门类组件的历程,叫做dependency
injection,是AngularJS的主导,作者会当第9回讲该过程。

3.2.1、生成导航元素

极有趣的的有,是运ng-repeat元素,为每个产品分类生成一个要素。

<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">

{{item}}

</a>

Ng-repeat属性值的第一部分,和扭转产品明细的同一,item in
data.products,它告诉ng-repeat指令要枚举data.products数组的对象,指派当前目标为叫做item的变量,并复制利用该令的a元素。

属于性值的亚有,告诉AngularJS,传递data.products数组被叫做orderBy的内建过滤器,用于排序数组。该orderBy过滤器,有一个参数,指定用于排序的性质。作者会于第14章完整地描述orderBy过滤器。

提醒:注意作者以单引号之间,指定了属于性名。默认地,AngularJS假设表达式中的讳,指向scope中定义之变量。要指定一个静态值,不得不采取字符串,在Javascript中,需要单引号或对引号。

过滤器有一个坏硬的特征,通过 |
将他们链式调用。AngularJS按照过滤器排列的相继,应用他们。这意味,category属性在排序过后,才传递让unique过滤器。你省作者如何指定unique过滤器要操作的性质:

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

然的结果是,data.products数组传递给orderBy过滤器,它根据category属性排序。Ta排序过之数组传递让unique,它回到一个字符串数组,包含独一无二的category值———因为unique过滤处理过程中莫移价值的逐一,结果保持上只过滤器的排序。

提醒:作者可以倒使用过滤器的相继,结果一致。不同的凡,orderBy过滤器会作用为一个字符串数组,而非是product对象。orderBy过滤器设计用来操作对象的,但您得通过动orderBy:’toString()’,来排序字符串。不要忘记了引号,不然,AngularJS找一个叫toString的scope属性,而休是调用toString方法。

3.2.2、处理点击事件

作者在a元素上使用ng-click指令,可以对应用户的点击。AngularJS提供相同组外修令,作者用在第11章节介绍,它们恩能够在相应事件不时,简单地调用控制器行为。ng-click指令的讳,建议指明AngularJS在点击事件触发时,要开啊。

<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">

{{item}}

</a>

在app.html中生出少数单a元素。第一单是静态地,创建Home按钮,作者用户展示所有成品之兼具分类。在该因素中,作者设置ng-click指令,它调用一个控制器行为,叫做selectCategory,没有参数。后面作者会创造该作为,现在,重要的政工是念念不忘另一个a元素,它的selectCategory行为来一个item变量值作为参数。点击时,例如selectCategory(‘Category
#1’)。

3.3、选择分类    

在浏览器被点击分类按钮,不会见发生其它力量,因为ng-click指令设置为调用一个尚从来不概念的一言一行。当你尝试看一个休有的作为还是数量常常,AngularJS不会见埋怨。这让debugging带来了略微劳,因为展示错误结果,但也再也灵敏。作者以在第13段描述如何当再次甚的地方以控制器和它们的范围。

3.3.1、定义控制器

笔者要定义叫做selectCategory的作为,来响应用户指向分类按钮的点击。他莫像用行添加到顶级sportsStoreCtrl控制器上,那是一切应用提供行为与多少的地方。取而代之,作者新建一个控制器,专用于活列表和分类视图。controllers/productListControllers.js。

唤醒:你恐怕会见咨询我,为什么自己的控制器的讳,比过滤器的重规范。原因是,过滤器更通用,准备当旁以被引用。

angular.module("sportsStore")

.controller("productListCtrl", function ($scope, $filter) {

var selectedCategory = null;

$scope.selectCategory = function (newCategory) {

selectedCategory = newCategory;

}

$scope.categoryFilterFn = function (product) {

return selectedCategory == null ||

product.category == selectedCategory;

}

});

笔者调用app.html文件被定义的sportsStroe模块的controller方法(记住,一个参数的angular.module方法,以为这招来现有模块,而简单独参数的意,是创办一个新模块)。

欠控制器叫做productListCtrl,它定义一个称为selectCategory的一言一行。该控制器还定义了一个称呼categoryFilterFn的作为,它因为一个产品对象作为参数,它于尚未分类为入选,或出一个分拣为入选并且该产品输入其时时,返回true。

提醒:注意selectedCategory变量没有定义在scope上,它就是一个正常的JavaScript变量,意味着她不克起命访问还是view的数据绑定中。这样做的结果是,创建了selectCategory行为,用于安装分类,categoryFilterFn用于过滤产品对象,但吃入选的归类的底细,保持私出。作者以SportStore应用中,不依靠是特性。

3.3.2、应用控制器和过滤产品

作者不得不用ng-contorller指令,应用控制器到视图,让ng-click指令可以调用selectCategory行为。不然,包含ng-click指令的因素的回到,会是顶级sportsStoreCtrl控制器,它不带有该作为。

<!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="panel panel-default row" ng-controller="productListCtrl">

<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">

{{item}}

</a>

</div>

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

<div class="well"

ng-repeat="item in data.products | filter:categoryFilterFn">

<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>

</div>

</body>

</html>

笔者添加script元素,导入productListControllers.js文件,在蕴藏分类列表和活列表的视图部分,使用ng-controller指令来利用productListCtrl控制器。

以productListCtrl控制器放在sportsStorectrl控制器范围外,意味着作者可以用先进的controller
scope
inheritance,作者将在第13回解释。productListCtrl继承sportsStoreCtrl中定义的data.products数组和另外其它数据以及表现。这样做的补,是可以界定控制器功能以采取及之限量,更便于执行单元测试,放置组件间非预期的因。

别一个改动,是以变更产品明细的ng-repeat上配置:

<div class="well" ng-repeat="item in data.products | filter:categoryFilterFn">

AngularJS提供的一个内建的过滤器,叫做filter。它处理一个聚众,选择外带有的对象的子集。作者用在第14回描述该技能。通过在ng-repeat指令Bootstrap上运其,确保只有当前受入选的分类的产品展示。

3.4、高亮于入选的归类

用户可以点击分类按钮,来过滤产品,但绝非视觉反馈,哪个范磊为选中了。作者以为当选的归类下btn-primary
CSS
class。第一步,在控制器中补充加一个行事,它接受一个分类。如果它们是于选中的分类,返回CSS
class名。

提拔:注意作者怎么当AngularJS模块上链式调用方法。这是因Module定义之章程,同样返回Module,这点以及fluent
API一样。

angular.module("sportsStore")

.constant("productListActiveClass", "btn-primary")

.controller("productListCtrl", function ($scope, $filter, productListActiveClass) {

var selectedCategory = null;

$scope.selectCategory = function (newCategory) {

selectedCategory = newCategory;

}

$scope.categoryFilterFn = function (product) {

return selectedCategory == null ||

product.category == selectedCategory;

}

$scope.getCategoryClass = function (category) {

return selectedCategory == category ? productListActiveClass : "";

}

});

笔者不思以表现代码里指定class的名字,所以作者以于Moduled对象上利用constant方法,定义一个称呼productListActiveClass的固定值。它同意自己转class,每次只要运时。要当控制器中走访该值,作者不得不声明constant名字作依靠,如下面那样。

.controller("productListCtrl", function ($scope, $filter, productListActiveClass) {

随之,可以以getCategoryClass行为遭到,使用productListActiveClass,它大概地检查她接受及的归类,返回class
名字或空字符串。

getCategoryClass行为看起有点始料未及,但它们好以每个分类导航按钮中调用,传递分类的名作参数。要采取CSS
class,作者利用ng-class指令。

<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>

Ng-class属性,它会动geCategoryClass返回的class 名。作者以以第11章节讲。

3.5、添加分页

分页,一蹩脚展示得数额之活。需要三步才会兑现分页:修改控制器,scope跟踪分页状态,实现过滤器,更新视图。

3.5.1、更新控制器

每当productListControllers.js文件被,更新控制器,以钉分页。

angular.module("sportsStore")

.constant("productListActiveClass", "btn-primary")

.constant("productListPageCount", 3)

.controller("productListCtrl", function ($scope, $filter,

productListActiveClass, productListPageCount) {

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 : "";

}

});

每页产品的数量给定义也常量,叫做productListPageCount。在控制器里,作者在scope上定义变量,曝露常量值(所以作者能于view上看它)和时于挑的页。作者都定义了行为,selectPage,允许受捎的页,编程其他页。getPageClass,设计用来ng-class指令,来高亮被入选的页,和前面受选中的分类一样。

提醒:你恐怕会见纳闷,为什么视图不能够直接访问常量值,而是使经scope来适合地露出。答案是,AngularJS努力放置组件间紧密地耦合,作者以第3章描述了。如果视图可以一直看服务及常量值,那么他好了无停歇的耦合和仰,难被测试与保障。

3.5.2、实现过滤

在customFilters.js文件被开创两个新的过滤器。

angular.module("customFilters", [])

.filter("unique", function () {

return function (data, propertyName) {

if (angular.isArray(data) && angular.isString(propertyName)) {

var results = [];

var keys = {};

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

var val = data[i][propertyName];

ChAPTeR6 ■SPORTSSTORe: A ReAl APPlICATIOn

144

if (angular.isUndefined(keys[val])) {

keys[val] = true;

results.push(val);

}

}

return results;

} else {

return data;

}

}

})

.filter("range", function ($filter) {

return function (data, page, size) {

if (angular.isArray(data) && angular.isNumber(page) && angular.isNumber(size)) {

var start_index = (page 1) * size;

if (data.length < start_index) {

return [];

} else {

return $filter("limitTo")(data.splice(start_index), size);

}

} else {

return data;

}

}

})

.filter("pageCount", function () {

return function (data, size) {

if (angular.isArray(data)) {

var result = [];

for (var i = 0; i < Math.ceil(data.length / size) ; i++) {

result.push(i);

}

return result;

} else {

return data;

}

}

});

第一独新的过滤器,叫做range,从一个数组中回到一批列元素,代表产品页。过滤器接收参数,当前给选中的页(用于表明range的初步index),和page
size(用于表明end index)。

Range过滤器不是特别幽默,不同于自身都在功能及提供的一个舅修过滤器,叫做limitTo,它起一个数组中,返回指定编号的项。要运该过滤器,我又$filter服务达到定义了一个依赖。它吃笔者创建并以filter的实例。作者用以地14章节讲详细,最紧要的凡即刻词声明:

return $filter("limitTo")(data.splice(start_index), size);

作者采取的JavaScript
标准措施splice的结果是择数据数组的等同组成部分,然后以它传递给limitTo过滤器,来选要当该页显示的。limitTo过滤器确保遍历数组没有问题,如果指定的数字不可用,会回到几独items。

老二独过滤器,pageCount,是一个污秽的——但好的——hack。Ng-repeat指令使得生成内容十分简单,但其根据数数组工作。你免可知,例如,让他还指定的次数。作者的过滤器计算出页码的一个数组。

警惕:作者为过滤器的效应绕开给限制的ng-repeat指令,这是深危险的,但是单应急道。更好之代方式,会在第16,17节看到,让ng-repeat指令生成指定次数的元素。

3.5.3、更新视图

更新app.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"]);

</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="panel panel-default row" ng-controller="productListCtrl">

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

<a ng-click="selectCategory()"

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

ChAPTeR6 ■SPORTSSTORe: A ReAl APPlICATIOn

146

<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>

第一处于变,是ng-repeat指令,生成产品列表,数据会通过range过滤器,过滤选择时页的产品。当前页的有心人,和每页产品之数据,使用作者定义在控制器scope上值作为参数,传递让过滤器。

老二远在改动,是互补加了导航按钮。作者利用ng-repeat指令,计算出目前当选的归类,有些许页产品,传递结果吃pageCount过滤器,这会导致ng-repeat指令生成是数量之导航页按钮。当前选中的页,通过ng-class指令表达,通过ng-click指令,页面变化。

 

 

 

 

 

 

 

 

 

相关文章