Angularjs+bootstrap购物车

上一篇文章学习如何简单开始一个Angular程序,跟着网上的教程我也来实现一个购物车功能,为了减少页面样式设计我使用了bootstrap来偷懒,下面先来看看我做的demo,效果图如下:

购物车
一、代码

如果看了这个效果还有兴趣想知道怎么做出来的话,那就继续往下看吧。,废话补多少,直接上代码。
代码地址: https://github.com/wangqingqiang/Angularjs
html代码:
<pre>
<!DOCTYPE html>
<html lang="en" ng-app="cart">
<head>
<meta charset="UTF-8">
<title>购物车</title>
<link rel="stylesheet" href="../scripts/angular-1.4.0-rc.2/docs/components/bootstrap-3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="main.css">
</head>
<body ng-controller="cartCtr">
<table class="table table-hover" ng-show="items.length">
<caption>AngularJS实现购物车</caption>
<tr>
<th>序号</th>
<th>商品信息</th>
<th>单价(元)</th>
<th>数量</th>
<th>金额(元)</th>
<th>操作</th>
</tr>
<tr ng-repeat=" item in items">
<td>{{$index + 1}}</td>
<td><a href="{{item.linkUrl}}" target="_blank" title="此链接将跳转到淘宝相关页面...">{{item.title}}</a></td>
<td class="bold">{{item.price|number:2}}</td>
<td>
<button type="button" class="btn btn-default btn-xs" ng-click="reduce(item.id)" ng-disabled="item.quantity<=1">-</button>
<input type="text" size="5" ng-model="item.quantity" ng-keydown="quantityKeydown()" ng-keyup="quantityKeyup()">
<button type="button" class="btn btn-default btn-xs" ng-click="add(item.id)">+</button>
</td>
<td class="bold mark">{{item.price*item.quantity|number:2}}</td>
<td>
<button type="button" class="btn btn-default btn-xs" ng-click="delete(item.id)">删除</button>
</td>
</tr>
</table>
<div class="empty" ng-show="!items.length">购物车空空,快去寻找宝贝</div>
<div class="total">
已选商品:<span>{{getQuantites()}} </span>
合计:
<span class=" mark" ng-show="getTotalAmount()<15000">{{getTotalAmount()|number:2}}</span>
<span class=" mark" ng-show="getTotalAmount()>=15000">
{{getTotalAmount()*discount|number:2}}<span class="btn btn-xs">(9折)</span>
<span class="discount">({{getTotalAmount()|number:2}})</span>
</span>
<button type="button" class="btn btn-primary btn-sm" ng-click="alertSubmit()">结 算</button>
</div>
<script src="../scripts/angular-1.4.0-rc.2/angular.js"></script><script src="app.js"></script>
</body>
</html>
</pre>
js代码:
<pre>
/* Created by wqq on 2016/5/25. */
var cartModule = angular.module('cart', []);
cartModule.controller('cartCtr', ['$scope', function ($scope) {
$scope.discount = 0.9;
$scope.items = [{id: 10001,title: "Web全栈工程师的自我修养 余果", price: 40.80,quantity: 2,linkUrl: "https://detail.tmall.com/item.htm?spm=a1z0d.6639537.1997196601.4.cwywJs&id=532166746631"},
{id: 10002,title: "MacBook Pro Retina 15英寸", price: 16088.00,quantity: 1,linkUrl: "https://detail.tmall.com/item.htm?spm=a1z0d.6639537.1997196601.26.cwywJs&id=45771116847"},
{id: 10003,title: "Surface Book I5 128G 独显",price: 11088.00, quantity: 1,linkUrl: "https://detail.tmall.com/item.htm?spm=a1z0d.6639537.1997196601.15.cwywJs&id=525614504276"},
{id: 10004, title: "Lenovo Yoga3Pro I5 4G",price: 7299.00, quantity: 1,linkUrl: "https://detail.tmall.com/item.htm?spm=a1z0d.6639537.1997196601.37.cwywJs&id=41541519814"} ];
$scope.add = function (id) {
angular.forEach($scope.items, function (item, index, array) {
if (item.id === id) {item.quantity++;} })
};
$scope.reduce = function (id) {
angular.forEach($scope.items, function (item, index, array) {
if (item.id === id) {item.quantity--; } })
};
//输入框添加keydown事件,避免输入非正整数
$scope.quantityKeydown = function (event) {
event = event || window.event;
var target=event.target||event.srcElement;
var keycode = event.keyCode;
if ((37 <= keycode && keycode <= 40)||(48 <= keycode && keycode <= 57) || (96 <= keycode && keycode <= 105) || keycode == 8) {
//方向键↑→ ↓←、数字键、backspace
}
else {
console.log(keycode);
event.preventDefault();
return false;
}
};
//keyup事件,当输入数字为0时,重新刷新文本框内容
$scope.quantityKeyup = function (event) {
event = event || window.event;
var target=event.target||event.srcElement;
var keycode = event.keyCode;
if (48 === keycode || 96 === keycode ) {
target.value=parseInt(target.value);
}};
//删除商品
$scope.delete = function (id) {
$scope.items.forEach(function (item, index) {
if (item.id == id) {
if (confirm("确定要从购物车中删除此商品?")) {
$scope.items.splice(index, 1);
return;
}
}
})
};
//计算已选商品数量
$scope.getQuantites = function () {
var quantities = 0;
angular.forEach($scope.items, function (item, index, array) {
if (item.quantity) {
quantities += parseInt(item.quantity);
}
});
return quantities;
};
//计算合计总金额
$scope.getTotalAmount = function () {
var totalAmount = 0;
angular.forEach($scope.items, function (item, index, array) {
totalAmount += item.quantity * item.price; });
return totalAmount;
};
$scope.alertSubmit = function () {alert("Angular实现购物车"); }
}]);
</pre>

二、分析

请忽略bootstrap的样式,我们只关注Angular,代码很简单,我们来简单的分析一下:

1. 准备

首先我们我们定义了一个cart模块、cartCtr控制器,并将它们引入到了html代码中,同时我们还在js中定义了一个数组items用于模拟购物车内的东西。

2. ng-repeat迭代器

为了将items里的数据动态的遍历加载出来,我们使用Angular里的内置指令ng-repeat,它可以非常方便的遍历数组,生成DOM元素,在这里循环生成了4个<tr>标签:
  <code><tr ng-repeat=" item in items"></code>
  item就是items数组里面的某一个对象,是不是感觉这就是js中的for/in循环~~如果你是一名.net开发人员,用过asp.net mvc的Razor就对这种其他语言无缝操作DOM元素很熟悉了,至于java、PHP是否有没有类似的语法我就不清楚了,我是一名苦逼的.net开发。

ng-repeat迭代器

  我们可以看到第一个td中用到了$index,这是ng-repeat内的,并不是我们定义的,它的值是当前item在items中的索引,从0开始,所以我们用$index+1作为序号,其他的(类似item.linkUrl)数据绑定在上一节中已经介绍过了。
  我们在单价和金额两列用到了{{ xxx|number:2}},这是Angular中的一种过滤器,作用是将前面的值xxx保留两位小数,金额嘛,我们当然要精确一些。刚才说了这是一种过滤器,那就还有其他的,比如currency,可以在xxx前面添加一个$符号表示美元,可以自行百度其他过滤器用法。

3. 添加事件

当前界面上分别有数量+、-按钮、删除按钮,这几个事件都比较简单,利用ng-click给元素添加点击事件。通过传递某个商品的id,找到这个商品,对这个商品进行加、减、删除操作,只不过在“-”按钮上有添加了一个ng-disabled标签,根据名字我们就可以很容易想到html的disabled属性,它的作用就是当ng-disabled的值为true时DOM元素禁用,同理,下面用到的ng-show也是一样的,true时显示,false时隐藏。如果是数字的话会自动转化为boolean值,0是false,非0是true,注意负数也是true!。这里我让当数量为1时就不能减少了,因为再少就可以直接删除了呀~
  然后在input元素添加ng-keydown事件,使其只能输入方向键↑→ ↓←、数字键、backspace。然后我试了下确实到达了目的,但是却可以输入类似“00021”这种数字,显然这并不能令人满意。我看了看淘宝的购物车,发现当在前面输入0时,这个文本框的内容会自动刷新,去掉前面的0,于是我又添加了ng-keyup事件:
<pre>
$scope.quantityKeyup = function (event) {
event = event || window.event; //兼容IE8以下,target也是
var target=event.target||event.srcElement;
var keycode = event.keyCode;
if (48 === keycode || 96 === keycode ) {
target.value=parseInt(target.value);
}};
</pre>

这时当我输入0时,文本框值就会自动刷新,为什么不添加到keydown里面而要另外再加一个事件呢?那是因为触发keydown事件时target.value的值还是原来的值,还没有包含本次输入的按键,而在keydown之后值就是新值了,这时候我们接着让触发keyup事件就可以达到目的了,可以对照看下淘宝购物车的效果,我觉得我的体验比它的更好,因为它只要不是在最后输入数字文本框总是会失去焦点。。。

4. 统计

统计数量就是直接绑定方法,遍历数组返回值。
  合计金额这块,我做了个满15000打9折的设计。利用ng-show隐藏显示带打折信息的合计金额。

三、总结

js中用到了几处forEach遍历数组,ECMAScript5中原生的方法是array.forEach(function(item,index,array){});
angular中也封装了,angular.forEach(array,function(item,index,array){});
代码中我两种方法都用到了,也不知道那种性能好。。
至此,购物车就已经完成了,利用Angular的双向绑定,可以快速的实现数量、金额的联动改变。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,671评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,442评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,524评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,623评论 1 275
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,642评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,584评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,953评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,621评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,865评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,608评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,698评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,378评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,958评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,940评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,173评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,419评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,425评论 2 342

推荐阅读更多精彩内容