8.1:SportsStore:Orders and Administration

本章,作者将通过收集和验证购物明细,来完成SportsStore应用,并在Deployd服务器上存储该订单。作者也构建了一个管理应用,允许认证用户查看订单,和管理产品分类。

1、准备实例项目

2、获取产品明细

在给用户显示购物车中的产品汇总后,作者将购物明细用于订单。这需要使用AngularJS的表单特性,你会在大多数web应用中需要。作者已经创建Views/placeOrder.html文件,捕获用户的购物明细。作者将介绍一些与表单相关度的特性,来避免重复大量相似的代码。作者首先添加一组数据属性(用户的名字和街道地址),然后再其他添加的属性。下面是placeOrder.html文件。

<h2>Check out now</h2>
<p>Please enter your details, and we‘ll ship your goods right away!</p>
<div class="well">
<h3>Ship to</h3>
<div class="form-group">
<label>Name</label>
<input class="form-control" ng-model="data.shipping.name" />
</div>
<h3>Address</h3>
<div class="form-group">
<label>Street Address</label>
<input class="form-control" ng-model="data.shipping.street" />
</div>
<div class="text-center">
<button class="btn btn-primary">Complete order</button>
</div>
</div>

第一件事情,是作者没有使用ng-controller指令,来为视图指定一个控制器。这意味着视图会被顶级controller ,sportsStoreCtrl支持,该控制器管理包含ng-view指令的视图。当视图不需要任何额外控制器行为时,不用为部分视图定义控制器。

这里,AngularJS的一个重要特性是,在input元素上,使用ng-model指令。

<input class="form-control" ng-model="data.shipping.name"/>

该ng-model指令,设置了一个双向数据绑定。作者会在第10章深入解释数据绑定,但这里简短介绍。它使用{{}},是单向绑定,意味着简单地从scope显示一个值。该单向绑定的值可以被过滤,它可以是一个表达式,而不仅仅是一个数据值,单他是一个只读关系。如果scope改变,显示的值也会变。它只能从scope到binding。

双向数据绑定用于表单元素,允许用户输入值,改变scope。更新流在scope和data binding之间。通过一个JavaScript功能,执行更新scope数据属性。这里只要知道,如果用户在input元素中输入了一个值,该值被指派到通过ng-model指令指定的scope属性上,在本例中是,data.shipping.name属性和data.shipping.street属性。

提示:注意作者没有必要更新控制器,让他在scope上定义一个data.shipping对象,或单独的name或street属性。如果该属性不存在,AngularJS scopes会假设你想动态地定义一个属性。作者将在第13章深入讲。

 

2.1、添加表单校验

如果你写过任何种类的web应用,使用表单元素,你会知道用户会在input字段中输入任何东西。要确保你得到预期的数据,AngularJS支持form validation,允许检查值。

AngularJS表单校验,基于标准HTML属性,应用到表单元素,例如type和required。表达那校验会自动执行,但必须显示校验feedback给用户。

提示:HTML5在input元素上定义了一组新的type属性的值,能被用于该值是一个e-mail地址或一个数字。作者会在第12章解释。

 

2.1.1、准备校验

设置表单校验的第一步,是添加一个form元素到视图,并添加校验属性到作者的input元素。下面是placeOrder.html文件。

<h2>Check out now</h2>
<p>Please enter your details, and we‘ll ship your goods right away!</p>
<form name="shippingForm" novalidate>
<div class="well">
<h3>Ship to</h3>
<div class="form-group">
<label>Name</label>
<input class="form-control" ng-model="data.shipping.name" required />
</div>
<h3>Address</h3>
<div class="form-group">
<label>Street Address</label>
<input class="form-control" ng-model="data.shipping.street" required />
</div>
<div class="text-center">
<button class="btn btn-primary">Complete order</button>
</div>
</div>
</form>

form元素有三个目的,作者没有使用浏览器为提交表单提供的内建支持的。

第一个目的,是启用校验。AngularJS使用自定义的指令,重定义了一些HTML元素,来启用制定特性,例如form。没有form元素,AngularJS不验证元素的内容,如input,select,textarea等。

第二个谜底,form元素禁用浏览器可能会尝试执行的任何校验,它通过novalidate属性。该属性是标准的HTML5特性,它确保只有AngularJS检查数据。如果你忽略了novalidate属性,那么用户可能会得到冲突,或多次校验feedback,基于浏览器的会被使用。

最后的目的,form元素会定义一个变量,用于报告表单校验。它是通过name属性做的,作者将它设为shippingForm。当用户点击button按钮,用户表单的内容被验证时,该值用于显示feedback。

 

2.1.2、显示验证Feedback

一旦form元素和校验属性放置好,AngularJS开始校验用户提供的数据,作者要为用户显示feedback。作者将在第12章详细讲解,但这里作者会用两种类型的feedback:定义CSS样式给AngularJS通过验证或没有通过验证的表单元素。可以使用scope变量控制feedback消息显示的元素的可见性。placeOrder.html文件:

<style>
.ng-invalid { background-color: lightpink; }
.ng-valid { background-color: lightgreen; }
span.error { color: red; font-weight: bold; }
</style>
<h2>Check out now</h2>
<p>Please enter your details, and we‘ll ship your goods right away!</p>
<form name="shippingForm" novalidate>
<div class="well">
<h3>Ship to</h3>
<div class="form-group">
<label>Name</label>
<input name="name" class="form-control"
ng-model="data.shipping.name" required />
<span class="error" ng-show="shippingForm.name.$error.required">
Please enter a name
</span>
</div>
<h3>Address</h3>
<div class="form-group">
<label>Street Address</label>
<input name="street" class="form-control"
ng-model="data.shipping.street" required />
<span class="error" ng-show="shippingForm.street.$error.required">
Please enter a street address
</span>
</div>
<div class="text-center">
<button class="btn btn-primary">Complete order</button>
</div>
</div>
</form>

AngularJS会给表单元素制定ng-valid和ng-invalid classes。Form元素总是会持有这些样式中的一个。

该CSS样式表明校验的效果。所以作者必须给每个元素添加一个name属性,使用AngularJS添加到scope的校验数据,控制error消息的可见性。

<input name="street"class="form-control" ng-model="
data.shipping.street" required />
<span class="error" ng-show="
shippingForm.street.$error.required">
Please enter a street address
</span>

input元素,用于捕获用户的街道,将name属性指派为street。AngularJS在scope上创建一个shippingForm.street对象(它是form元素的name和input元素的name的结合)。该对象定义一个$error属性,该属性是一个对象,为每个校验属性提供一个属性,表示input元素失败的原因。如果shippingForm.street.$error.required属性为真,知道street input元素通过没有校验通过,用于显示error消息给用户的空间,会执行ng-show指令。

记住:作者用的简单,但AngularJS可以用于创建更复杂的校验。

 

2.1.3、将按钮链接到校验

在多数web应用,用户在提供所有表单数据,并校验通过后,才能进入下一步。当表单没有通过校验,作者想禁用Complete order按钮,并在用户完成表单属性后,自动启用它。

要做到这点,作者要改进AngularJS添加到scope的校验信息。作者可以得到整个表单的状态。当input元素为没有通过校验,shippingForm.$invalid属性为true,作者将基于此,使用ng-disabled指令,管理按钮元素的状态。作者将在第11章描述ng-disable指令。

<div class="text-center">
<button ng-disabled="shippingForm.$invalid"
class="btn btn-primary">Complete order</button>
</div>

 

2.2、添加剩下的表单字段

现在你知道AngularJS是怎么进行表单校验的了,作者要将剩下的input元素添加到表单。

<style>
.ng-invalid { background-color: lightpink; }
.ng-valid { background-color: lightgreen; }
span.error { color: red; font-weight: bold; }
</style>
<h2>Check out now</h2>
<p>Please enter your details, and we‘ll ship your goods right away!</p>
<form name="shippingForm" novalidate>
<div class="well">
<h3>Ship to</h3>
<div class="form-group">
<label>Name</label>
<input name="name" class="form-control"
ng-model="data.shipping.name" required />
<span class="error" ng-show="shippingForm.name.$error.required">
Please enter a name
</span>
</div>
<h3>Address</h3>
<div class="form-group">
<label>Street Address</label>
<input name="street" class="form-control"
ng-model="data.shipping.street" required />
<span class="error" ng-show="shippingForm.street.$error.required">
Please enter a street address
</span>
</div>
<div class="form-group">
<label>City</label>
<input name="city" class="form-control"
ng-model="data.shipping.city" required />
<span class="error" ng-show="shippingForm.city.$error.required">
Please enter a city
</span>
</div>
<div class="form-group">
<label>State</label>
<input name="state" class="form-control"
ng-model="data.shipping.state" required />
<span class="error" ng-show="shippingForm.state.$error.required">
Please enter a state
</span>
</div>
<div class="form-group">
<label>Zip</label>
<input name="zip" class="form-control"
ng-model="data.shipping.zip" required />
<span class="error" ng-show="shippingForm.zip.$error.required">
Please enter a zip code
</span>
</div>
<div class="form-group">
<label>Country</label>
<input name="country" class="form-control"
ng-model="data.shipping.country" required />
<span class="error" ng-show="shippingForm.country.$error.required">
Please enter a country
</span>
</div>
<h3>Options</h3>
<div class="checkbox">
<label>
<input name="giftwrap" type="checkbox"
ng-model="data.shipping.giftwrap" />
Gift wrap these items
</label>
</div>
<div class="text-center">
<button ng-disabled="shippingForm.$invalid"
class="btn btn-primary">Complete order</button>
</div>
</div>
</form>

提示:你可能会尝试使用ng-repeat指令,来生成input元素。这样生成的,不能很好滴工作,因为有些指令属性值,如ng-model,ng-show,是计算过的。作者建议这样做,单你想用更先进的技术,看第15-17章,作者会描述创建自定义指令的方式。

 

3、存储订单

本节,我们会扩展Deployd服务器提供的数据库,使用Ajax请求发送订单数据给服务器,在流程的最后,播放一个感谢信息。

 

3.1、扩展Deployd服务器

使用dashboard,选择collection,将集合的名字设为/orders,点击创建按钮。定义如下属性:

Name Type Required

name string Yes

street string Yes

city string Yes

state string Yes

zip string Yes

country string Yes

giftwrap boolean No

products array Yes

 

多花点注意,在gifwrap和products属性的类型上。

 

3.2、定义控制器行为

下一步要定义使用Ajax请求,发送订单给Deployd服务器的控制器行为。我可以以很多不同的方式定义该功能——创建一个服务或创建一个新的控制器。你可以以你喜欢的方式,构建,没有绝对的对与错。作者将保持一切都很简单,添加行为到顶级sportsStore控制器上,该控制器已经包含用Ajax请求加载产品数据的代码。

angular.module("sportsStore")
.constant("dataUrl", "http://localhost:5500/products")
.constant("orderUrl", "http://localhost:5500/orders")
.controller("sportsStoreCtrl", function ($scope, $http, $location,
dataUrl, orderUrl, cart) {
$scope.data = {
};
$http.get(dataUrl)
.success(function (data) {
$scope.data.products = data;
})
.error(function (error) {
$scope.data.error = error;
});
$scope.sendOrder = function (shippingDetails) {
var order = angular.copy(shippingDetails);
order.products = cart.getProducts();
$http.post(orderUrl, order)
.success(function (data) {
$scope.data.orderId = data.id;
cart.getProducts().length = 0;
})
.error(function (error) {
$scope.data.orderError = error;
}).finally(function () {
$location.path("/complete");
});
}
});

Depolyd会在数据库中创建一个新的对象,来相应POST请求,并返回刚刚创建的对象,包括id属性。

作者定义了一个新的constant,制定URL,你会用于POST请求,并添加一个cart服务的依赖,以得到产品明细。作者添加到控制器的行为叫做sendOrder,它将用户的购物明细,作为参数。

作者使用angular.copy工具方法,他在第5章描述过,用来创建shipping details对象的拷贝,所以他可以安全地操作它,而不影响应用的其他部分。该shipping details对象的属性,通过ng-model指令创建,代表作者的orders Deployd集合。左右要做的,就是定义一个products属性,来引用购物车中的products数组。

作者使用$http.post方法,创建一个Ajax POST请求指定URL和数据,他使用success和error方法,在第5章介绍过,来相应请求的结果。

作者也使用在$http.post方法返回的promise上,使用then方法。该then方法,持有一个功能,无论Ajax请求的结果如何,都会被调用。无论发生什么,他都想要显示相同的视图给用户,所以他使用then方法,调用$location.path方法。这是编程的方式设置URL的path组建,它将会通过第7章创建的URL配置,触发视图改变。

 

3.3、调用控制器行为

要调用心控制器行为,必须添加ng-click指令到shipping details视图的button元素。

<div class="text-center">
<button ng-disabled="shippingForm.$invalid"
ng-click="sendOrder(data.shipping)"
class="btn btn-primary">
Complete order
</button>
</div

 

3.4、定义视图

作者定义的Ajax请求完成后的URL路径是/complete,该URL路由配置映射到/views/thankYou.html文件:

<div class="alert alert-danger" ng-show="data.orderError">
Error ({{data.orderError.status}}). The order could not be placed.
<a href="#/placeorder" class="alert-link">Click here to try again</a>
</div>
<div class="well" ng-hide="data.orderError">
<h2>Thanks!</h2>
Thanks for placing your order. We‘ll ship your goods as soon as possible.
If you need to contact us, use reference {{data.orderId}}.
</div>

该视图定义了两个不同的内容块,来处理Ajax请求的success和unsuccessful。如果发生error,error的明细会显示,通过一个a链接,让用户返回到shipping details视图,让他可以再试一次。如果请求成功,为用户显示thank-you消息,包含新订单对象的id。

4、做改进

以后会做改进的地方:

第一,当你加载app.html文件到浏览器,你可能注意到一个小的延迟,在视图显示和products的元素和分类被生成。这是因为Ajax请求在后台获取数据,在等待服务器返回数据旗舰,AngularJS继续执行应用,并显示视图,当数据抵达后,在更新。在第22章,作者将描述如何使用URL路由特性,让AngularJS在Ajax请求已经完成后,再显示视图。

第二,作者将产品数据中的分类,提取出来,用于导航和分页特性。在一个真实的项目中,作者会考虑,在产品数据第一次抵达时,生成该信息。在第20章,作者描述如何使用promises,来构建行为链。

最后,作者使用$animate服务,在第23章介绍,当URL路径改变时,在视图切换时使用过度动画。

4.1、避免优化陷阱

你注意到,作者说要考虑重用分类和分页数据。这是因为任何类型的优化,都要十分小心,来确保它是明智的,并避免两个主要的陷阱。

第一个陷阱是,过早地优化。

第二个陷阱是,translation优化。

 

5、管理产品分类

要完成SportsStore应用,作者要创建一个应用,来允许管理员管理产品分类的内容,和订单队列。这回允许作者,演示AngularJS如何用于执行create,read,update,delete操作。

记住:每个后端服务实现认证有不同的方式,但基本的前提是相同的:将用户的凭证,通过请求发送给制定URL,如果请求成功,浏览器会返回一个cookie,浏览器会在随后的请求中,自动发送该cookie,用以标识用户。本例中使用Deployd。

 

5.1、准备Deployd

给数据库做一些改变,有些事情只能管理员做。在这里,我们会使用Deployd定义一个管理员用户,并穿件访问策略。

Collection Admin User
products create,read,update,delete read
orders create,read,update,delete create

总之,管理员能在任何集合上执行任何操作。一般用户可以read产品集合,并创建orders集合中的新对象。

在dashboard上创建Users Collection ,设置新容器的名字为/users。

用户集合,定义了id,username,password属性,是作者需要的。创建一个新对象,用户名密码为admin,secret。

 

5.1.1、集合的安全

作者喜欢Deployd的一个特性,是他定义一个简单的JavaScript API,可以用于实现服务端功能。当在一个集合上执行操作时,一系列的事件会被触发。点击products集合的Events,你会看到一系列的tab,代表不同的集合事件:On Get,On Validate,On Post,On Put,On Delete。所有的集合都有这些事件,你可以使用JavaScript,来加强认证策略。填入一下代码到On Put和On Delete tabs:

if (me === undefined || me.username != "admin") {
    cancel("No authorization", 401);
}

在Deployd API中,变量me,代表当前用户,cancel功能,使用制定消息和HTTP状态吗,结束一个请求。该代码,允许有权限的用户,和管理员用户访问,单其他请求都会用401状态吗结束,这代表客户端是没有权限做请求。

提示:不要担心这些事件是什么,作者会在后面说。

重复这些步骤,在orders集合的事件里,处理On Post和On Validate。强制执行认证控制的事件:

Collection Description

products On Put, On Delete

orders On Get, On Put, On Delete

users None

 

5.2、创建管理应用

作者要为管理任务,创建一个分隔的AngularJS应用。作者可以在主应用中集成这些特性,但这将意味着所有用户都将下载admin功能的代码,即使他们永远用不上。作者添加一个新文件,叫admin.html,放到angularjs文件夹:

<!DOCTYPE html>
<html ng-app="sportsStoreAdmin">
<head>
<title>Administration</title>
<script src="angular.js"></script>
<script src="ngmodules/angular-route.js"></script>
<link href="bootstrap.css" rel="stylesheet" />
<link href="bootstrap-theme.css" rel="stylesheet" />
<script>
angular.module("sportsStoreAdmin", ["ngRoute"])
.config(function ($routeProvider) {
$routeProvider.when("/login", {
templateUrl: "/views/adminLogin.html"
});
$routeProvider.when("/main", {
templateUrl: "/views/adminMain.html"
});
$routeProvider.otherwise({
redirectTo: "/login"
});
});
</script>
</head>
<body>
<ng-view />
</body>
</html>

作者使用Module.config方法,创建了三个路由,让ng-view指令显示body元素。

URL Path View

/login /views/adminLogin.html

/main /views/adminMain.html

All others Redirects to /login

otherwise方法定义的路由,作者使用redirectTo选项,将URL路径改变到其他路由。这将让浏览器到/login路径,用于认证用户。作者将在第22章描述。

 

5.2.1、添加占位符视图

作者要实现认证特性,需要为/views/adminMain.html视图文件创建一些占位符内容,当认证成功时会显示。下面是adminMain.html文件的内容:

<div class="well">
This is the main view
</div>

作者将在后面替换它。

 

5.3、实现认证

Deployd认证用户,使用标准HTTP请求。该应用发送一个POST请求到/users/login URL,包含username和password值。如果认证成功,服务器响应状态吗200,如果失败,返回401。要实现认证,作者要定义一个控制器,发起Ajax调用,并处理响应。下面是controllers/adminControllers.js文件的内容:

angular.module("sportsStoreAdmin")
.constant("authUrl", "http://localhost:5500/users/login")
.controller("authCtrl", function($scope, $http, $location, authUrl) {
$scope.authenticate = function (user, pass) {
$http.post(authUrl, {
username: user,
password: pass
}, {
withCredentials: true
}).success(function (data) {
$location.path("/main");
}).error(function (error) {
$scope.authenticationError = error;
});
}
});

 

5.3.1、定义视图认证

5.4、定义主视图和控制器

5.5、实现订单特性

5.6、实现产品特性

5.6.1、定义RESTful控制器

5.6.2、定义视图

5.6.3、添加对HTML文件的引用

8.1:SportsStore:Orders and Administration,布布扣,bubuko.com

时间: 2024-10-06 23:23:47

8.1:SportsStore:Orders and Administration的相关文章

项目实战SportsStore——订单处理模块

前面的步骤如果顺利完成,你的网站运行之后应该能够正常显示下面三个页面: 1.产品列表 2.购物车内容页面 在某个商品后面点击“添加到购物车”则出现下图页面: 此页面上点击“继续购物”按钮则返回到产品列表页面(第一幅图所示). 如果你的网站没有看到这个页面,你需要对照参考教材“8.3 提交订单”之前的内容进行修改.这里给大家提供一个参考代码,请点击下载 提交订单模块 1.添加订单相关数据表,记录订单信息 提交购物车中的订单,需要将用户购买的商品写到数据库里面保存起来,以后发货.用户查看订单等操作时

生成订单:三个表(Products,Orders,OrderItem)

1.有三个表(Product上,Orders,OrderItem) 分别创建对应的三个实体类 OrderItem中有外键Order_id 参考Orders中的id :Product_id参考Product中的id:Orders中有外键User_id参考User中的id sql 代码: CREATE DATABASE day2017_04_05; USE day2017_04_05; CREATE TABLE `user` ( `id` INT(11) AUTO_INCREMENT, `usern

unable to lock the administration directory问题解决

E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it? 刚开始试了如下两个: sudo rm /var/cache/apt/archives/lock sudo rm /var/lib/dpkg/lock 发现没效果,然后有试了下面这组命令: sudo rm /var/lib/dpkg/lock sudo rm /var/lib/dpkg/lock sudo dpk

POJ1270 Following Orders[拓扑排序所有方案 Kahn]

Following Orders Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 4885   Accepted: 1973 Description Order is an important concept in mathematics and in computer science. For example, Zorn's Lemma states: ``a partially ordered set in which

统一者管理员指南(Unifier Administration Guide)中文

统一者管理员指南 Unifier Administration Guide 2014年6月 发布 2014年11月翻译 10.0版本 10.0.1译 关于译者 翻译者QQ:77811970 Email : [email protected] 更新:219296219(qq群) 由于译者水平有限,本文全面借助网络翻译工具,并作适当的整理与排版,全文内容仅供参考学习使用,加入QQ群观注更新的版本,版本归属Oracle公司. 内容 关于译者... 1 内容.. 2 1.     开始.. 12 1.1

POJ - 1731 Orders

题意:排序后字符串全排列 思路:好久没水一题了 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 220; char str[MAXN]; int main() { while (scanf("%s", str) != EOF) { int n = strle

怎么把顶部的Django administration去掉!!

/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/django/contrib/admin/templates/admin 目录下,有个叫 base_site.html的文件,打开并把Django administration去掉即可

svcs (service status) 和 svcadm (service administration) 使用

1. svcs  显示服务实例的状态信息 svcs - report service status  显示服务状态命令 DESCRIPTION The svcs command displays information about service instances as recorded in the service configuration repository. 该命令显示记录在服务配置库中的服务实例信息 The first form of this command prints one

IIS 7管理API——Microsoft.Web.Administration介绍

原文:http://www.cnblogs.com/dflying/archive/2006/04/17/377276.html 本文翻译整理自Carlos Aguilar Mares的blog文章:Microsoft.Web.Administration in IIS 7. 请注意本文的内容均基于Windows Vista Beta 2版本,在正式的发布版本中可能会有所改变. Microsoft中提供了管理IIS7的一些非常强大的API--Microsoft.Web.Administratio