AngularJS6.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指令上利用它,确保唯有当前被入选的分类的成品显示。

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指令,页面变化。

 

 

 

 

 

 

 

 

 

相关文章