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

 

 

 

 

 

 

 

 

 

相关文章