|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. 引言
AngularJS作为一款由Google维护的前端JavaScript框架,以其强大的数据绑定和依赖注入特性,在构建单页应用(SPA)方面表现出色。电商应用是AngularJS的经典应用场景之一,而购物车功能则是电商应用的核心。本教程将带你从零开始,通过一个完整的购物车项目,深入理解AngularJS的数据绑定机制与状态管理方法,构建一个专业的电商应用购物车功能。
在开始之前,请确保你已经具备了基本的HTML、CSS和JavaScript知识,并对AngularJS有初步了解。我们将逐步构建这个项目,从基础功能到高级特性,让你全面掌握AngularJS在电商应用中的实际应用。
2. AngularJS基础与项目准备
2.1 AngularJS核心概念
在开始我们的购物车项目之前,让我们先回顾一些AngularJS的核心概念:
• 数据绑定:AngularJS的双向数据绑定是其最强大的特性之一。它允许模型(model)和视图(view)之间的自动同步,当模型数据发生变化时,视图会自动更新,反之亦然。
• 控制器(Controller):控制器是AngularJS应用中的JavaScript构造函数,用于设置作用域($scope)的初始状态并向作用域添加行为。
• 作用域(Scope):作用域是连接控制器和视图的桥梁,它是一个对象,包含应用的数据和函数。
• 服务(Service):服务是用于组织应用中可复用代码的单例对象,可以用于在控制器之间共享数据或功能。
数据绑定:AngularJS的双向数据绑定是其最强大的特性之一。它允许模型(model)和视图(view)之间的自动同步,当模型数据发生变化时,视图会自动更新,反之亦然。
控制器(Controller):控制器是AngularJS应用中的JavaScript构造函数,用于设置作用域($scope)的初始状态并向作用域添加行为。
作用域(Scope):作用域是连接控制器和视图的桥梁,它是一个对象,包含应用的数据和函数。
服务(Service):服务是用于组织应用中可复用代码的单例对象,可以用于在控制器之间共享数据或功能。
2.2 项目设置
首先,让我们创建项目的基本结构:
- angularjs-shopping-cart/
- ├── index.html
- ├── css/
- │ └── style.css
- ├── js/
- │ ├── app.js
- │ ├── controllers/
- │ │ ├── productController.js
- │ │ └── cartController.js
- │ ├── services/
- │ │ ├── productService.js
- │ │ └── cartService.js
- │ └── directives/
- │ └── productDirective.js
- └── data/
- └── products.json
复制代码
在index.html中,我们需要引入AngularJS库和我们自己的JavaScript文件:
- <!DOCTYPE html>
- <html lang="zh-CN" ng-app="shoppingCartApp">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>AngularJS 购物车</title>
- <link rel="stylesheet" href="css/style.css">
- <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
- <script src="js/app.js"></script>
- <script src="js/services/productService.js"></script>
- <script src="js/services/cartService.js"></script>
- <script src="js/controllers/productController.js"></script>
- <script src="js/controllers/cartController.js"></script>
- <script src="js/directives/productDirective.js"></script>
- </head>
- <body>
- <div class="container">
- <header>
- <h1>AngularJS 购物车示例</h1>
- </header>
-
- <div class="content">
- <!-- 商品列表区域 -->
- <div ng-controller="ProductController as productCtrl">
- <h2>商品列表</h2>
- <div class="product-list">
- <!-- 商品将通过AngularJS动态加载 -->
- </div>
- </div>
-
- <!-- 购物车区域 -->
- <div ng-controller="CartController as cartCtrl">
- <h2>购物车</h2>
- <div class="cart">
- <!-- 购物车内容将通过AngularJS动态加载 -->
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
复制代码
2.3 创建AngularJS应用模块
在js/app.js中,我们创建AngularJS应用模块:
- // 创建AngularJS应用模块
- var app = angular.module('shoppingCartApp', []);
- // 应用配置
- app.config(['$httpProvider', function($httpProvider) {
- // 配置HTTP请求
- $httpProvider.defaults.headers.common['Accept'] = 'application/json';
- $httpProvider.defaults.headers.post['Content-Type'] = 'application/json';
- }]);
- // 应用运行
- app.run(['$rootScope', function($rootScope) {
- // 初始化应用
- console.log('购物车应用已启动');
-
- // 全局错误处理
- $rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
- console.error('路由变更错误:', rejection);
- });
- }]);
复制代码
3. 商品展示功能
3.1 创建商品数据
首先,我们创建一些示例商品数据。在data/products.json文件中:
- [
- {
- "id": 1,
- "name": "iPhone 13 Pro",
- "description": "苹果最新款智能手机,配备A15仿生芯片",
- "price": 7999,
- "image": "images/iphone13pro.jpg",
- "category": "手机",
- "stock": 10
- },
- {
- "id": 2,
- "name": "MacBook Pro 14寸",
- "description": "配备M1 Pro芯片的专业级笔记本电脑",
- "price": 14999,
- "image": "images/macbookpro14.jpg",
- "category": "电脑",
- "stock": 5
- },
- {
- "id": 3,
- "name": "AirPods Pro",
- "description": "主动降噪无线耳机",
- "price": 1999,
- "image": "images/airpodspro.jpg",
- "category": "配件",
- "stock": 20
- },
- {
- "id": 4,
- "name": "iPad Air",
- "description": "轻薄强大的平板电脑",
- "price": 4399,
- "image": "images/ipadair.jpg",
- "category": "平板",
- "stock": 8
- },
- {
- "id": 5,
- "name": "Apple Watch Series 7",
- "description": "全天候健康监测智能手表",
- "price": 2999,
- "image": "images/applewatch7.jpg",
- "category": "穿戴设备",
- "stock": 15
- }
- ]
复制代码
3.2 创建商品服务
在js/services/productService.js中,我们创建一个服务来获取商品数据:
- // 商品服务
- app.service('ProductService', ['$http', '$q', function($http, $q) {
- var self = this;
-
- // 获取所有商品
- self.getProducts = function() {
- var deferred = $q.defer();
-
- $http.get('data/products.json')
- .then(function(response) {
- deferred.resolve(response.data);
- })
- .catch(function(error) {
- deferred.reject(error);
- console.error('获取商品数据失败:', error);
- });
-
- return deferred.promise;
- };
-
- // 根据ID获取商品
- self.getProductById = function(id) {
- var deferred = $q.defer();
-
- self.getProducts().then(function(products) {
- var product = products.find(function(p) {
- return p.id === id;
- });
-
- if (product) {
- deferred.resolve(product);
- } else {
- deferred.reject('未找到ID为 ' + id + ' 的商品');
- }
- }).catch(function(error) {
- deferred.reject(error);
- });
-
- return deferred.promise;
- };
-
- // 根据类别获取商品
- self.getProductsByCategory = function(category) {
- var deferred = $q.defer();
-
- self.getProducts().then(function(products) {
- var filteredProducts = products.filter(function(p) {
- return p.category === category;
- });
-
- deferred.resolve(filteredProducts);
- }).catch(function(error) {
- deferred.reject(error);
- });
-
- return deferred.promise;
- };
- }]);
复制代码
3.3 创建商品控制器
在js/controllers/productController.js中,我们创建一个控制器来管理商品展示:
- // 商品控制器
- app.controller('ProductController', ['ProductService', 'CartService', '$scope', function(ProductService, CartService, $scope) {
- var self = this;
-
- // 初始化变量
- self.products = [];
- self.loading = true;
- self.error = null;
- self.categories = [];
- self.selectedCategory = null;
-
- // 获取商品数据
- self.loadProducts = function() {
- self.loading = true;
- self.error = null;
-
- ProductService.getProducts()
- .then(function(products) {
- self.products = products;
- self.extractCategories();
- self.loading = false;
- })
- .catch(function(error) {
- self.error = '加载商品失败: ' + error;
- self.loading = false;
- });
- };
-
- // 提取商品类别
- self.extractCategories = function() {
- var categoriesMap = {};
-
- self.products.forEach(function(product) {
- categoriesMap[product.category] = true;
- });
-
- self.categories = Object.keys(categoriesMap);
- };
-
- // 根据类别筛选商品
- self.filterByCategory = function(category) {
- self.selectedCategory = category;
- };
-
- // 获取筛选后的商品
- self.getFilteredProducts = function() {
- if (!self.selectedCategory) {
- return self.products;
- }
-
- return self.products.filter(function(product) {
- return product.category === self.selectedCategory;
- });
- };
-
- // 添加商品到购物车
- self.addToCart = function(product) {
- CartService.addToCart(product, 1)
- .then(function() {
- // 显示成功消息
- self.showMessage(product.name + ' 已添加到购物车', 'success');
- })
- .catch(function(error) {
- // 显示错误消息
- self.showMessage('添加失败: ' + error, 'error');
- });
- };
-
- // 显示消息
- self.showMessage = function(message, type) {
- self.message = message;
- self.messageType = type;
-
- // 3秒后清除消息
- $timeout(function() {
- self.message = null;
- self.messageType = null;
- }, 3000);
- };
-
- // 初始化
- self.loadProducts();
- }]);
复制代码
3.4 创建商品指令
为了更好地展示商品,我们创建一个自定义指令。在js/directives/productDirective.js中:
- // 商品指令
- app.directive('productCard', function() {
- return {
- restrict: 'E', // 作为元素使用
- scope: {
- product: '=', // 双向绑定
- onAddToCart: '&' // 父作用域的方法
- },
- templateUrl: 'templates/product-card.html', // 模板URL
- controller: function($scope) {
- // 添加商品到购物车
- $scope.addToCart = function() {
- $scope.onAddToCart({product: $scope.product});
- };
- }
- };
- });
复制代码
创建模板文件templates/product-card.html:
- <div class="product-card">
- <div class="product-image">
- <img ng-src="{{product.image}}" alt="{{product.name}}" onerror="this.src='images/placeholder.jpg'">
- </div>
- <div class="product-info">
- <h3 class="product-name">{{product.name}}</h3>
- <p class="product-description">{{product.description}}</p>
- <div class="product-meta">
- <span class="product-category">{{product.category}}</span>
- <span class="product-stock">库存: {{product.stock}}</span>
- </div>
- <div class="product-price">
- ¥{{product.price | number:2}}
- </div>
- <button class="add-to-cart-btn" ng-click="addToCart()" ng-disabled="product.stock <= 0">
- {{product.stock > 0 ? '加入购物车' : '缺货'}}
- </button>
- </div>
- </div>
复制代码
3.5 更新商品列表视图
现在,我们更新index.html中的商品列表区域:
- <!-- 商品列表区域 -->
- <div ng-controller="ProductController as productCtrl">
- <h2>商品列表</h2>
-
- <!-- 加载状态 -->
- <div ng-if="productCtrl.loading" class="loading">
- <p>正在加载商品...</p>
- </div>
-
- <!-- 错误状态 -->
- <div ng-if="productCtrl.error" class="error">
- <p>{{productCtrl.error}}</p>
- </div>
-
- <!-- 消息提示 -->
- <div ng-if="productCtrl.message" class="message {{productCtrl.messageType}}">
- <p>{{productCtrl.message}}</p>
- </div>
-
- <!-- 类别筛选 -->
- <div class="category-filter">
- <button ng-class="{active: !productCtrl.selectedCategory}"
- ng-click="productCtrl.filterByCategory(null)">
- 全部商品
- </button>
- <button ng-repeat="category in productCtrl.categories"
- ng-class="{active: productCtrl.selectedCategory === category}"
- ng-click="productCtrl.filterByCategory(category)">
- {{category}}
- </button>
- </div>
-
- <!-- 商品列表 -->
- <div class="product-list">
- <product-card ng-repeat="product in productCtrl.getFilteredProducts()"
- product="product"
- on-add-to-cart="productCtrl.addToCart(product)">
- </product-card>
- </div>
- </div>
复制代码
3.6 添加样式
在css/style.css中添加一些基本样式:
现在,我们已经完成了商品展示功能。用户可以看到商品列表,按类别筛选商品,并将商品添加到购物车。接下来,我们将实现购物车功能。
4. 购物车功能实现
4.1 创建购物车服务
在js/services/cartService.js中,我们创建一个服务来管理购物车状态:
- // 购物车服务
- app.service('CartService', ['$q', '$rootScope', function($q, $rootScope) {
- var self = this;
-
- // 购物车数据
- self.cartItems = [];
- self.cartId = 'shopping_cart_' + Date.now(); // 简单的购物车ID
-
- // 初始化购物车
- self.initCart = function() {
- // 尝试从本地存储加载购物车数据
- var savedCart = localStorage.getItem(self.cartId);
-
- if (savedCart) {
- try {
- self.cartItems = JSON.parse(savedCart);
- } catch (e) {
- console.error('解析购物车数据失败:', e);
- self.cartItems = [];
- }
- }
-
- // 保存购物车到本地存储
- self.saveCart();
- };
-
- // 保存购物车到本地存储
- self.saveCart = function() {
- localStorage.setItem(self.cartId, JSON.stringify(self.cartItems));
-
- // 广播购物车更新事件
- $rootScope.$broadcast('cartUpdated', {
- count: self.getItemCount(),
- total: self.getTotal()
- });
- };
-
- // 添加商品到购物车
- self.addToCart = function(product, quantity) {
- var deferred = $q.defer();
-
- // 验证数量
- quantity = parseInt(quantity) || 1;
- if (quantity <= 0) {
- deferred.reject('数量必须大于0');
- return deferred.promise;
- }
-
- // 检查商品库存
- if (product.stock < quantity) {
- deferred.reject('库存不足');
- return deferred.promise;
- }
-
- // 检查购物车中是否已有该商品
- var existingItem = self.cartItems.find(function(item) {
- return item.product.id === product.id;
- });
-
- if (existingItem) {
- // 如果商品已在购物车中,增加数量
- var newQuantity = existingItem.quantity + quantity;
-
- // 再次检查库存
- if (product.stock < newQuantity) {
- deferred.reject('库存不足');
- return deferred.promise;
- }
-
- existingItem.quantity = newQuantity;
- } else {
- // 如果商品不在购物车中,添加新项
- self.cartItems.push({
- product: product,
- quantity: quantity,
- addedAt: new Date()
- });
- }
-
- // 保存购物车
- self.saveCart();
-
- deferred.resolve({
- message: '商品已添加到购物车',
- item: existingItem || self.cartItems[self.cartItems.length - 1]
- });
-
- return deferred.promise;
- };
-
- // 从购物车中移除商品
- self.removeFromCart = function(productId) {
- var deferred = $q.defer();
-
- var index = self.cartItems.findIndex(function(item) {
- return item.product.id === productId;
- });
-
- if (index !== -1) {
- var removedItem = self.cartItems.splice(index, 1)[0];
- self.saveCart();
-
- deferred.resolve({
- message: '商品已从购物车中移除',
- item: removedItem
- });
- } else {
- deferred.reject('购物车中找不到该商品');
- }
-
- return deferred.promise;
- };
-
- // 更新购物车中商品的数量
- self.updateQuantity = function(productId, newQuantity) {
- var deferred = $q.defer();
-
- // 验证数量
- newQuantity = parseInt(newQuantity) || 1;
- if (newQuantity <= 0) {
- // 如果数量小于等于0,则移除商品
- return self.removeFromCart(productId);
- }
-
- var item = self.cartItems.find(function(item) {
- return item.product.id === productId;
- });
-
- if (item) {
- // 检查库存
- if (item.product.stock < newQuantity) {
- deferred.reject('库存不足');
- return deferred.promise;
- }
-
- item.quantity = newQuantity;
- self.saveCart();
-
- deferred.resolve({
- message: '数量已更新',
- item: item
- });
- } else {
- deferred.reject('购物车中找不到该商品');
- }
-
- return deferred.promise;
- };
-
- // 清空购物车
- self.clearCart = function() {
- var deferred = $q.defer();
-
- self.cartItems = [];
- self.saveCart();
-
- deferred.resolve({
- message: '购物车已清空'
- });
-
- return deferred.promise;
- };
-
- // 获取购物车中的所有商品
- self.getCartItems = function() {
- return self.cartItems;
- };
-
- // 获取购物车中商品的总数量
- self.getItemCount = function() {
- return self.cartItems.reduce(function(count, item) {
- return count + item.quantity;
- }, 0);
- };
-
- // 计算购物车总价
- self.getTotal = function() {
- return self.cartItems.reduce(function(total, item) {
- return total + (item.product.price * item.quantity);
- }, 0);
- };
-
- // 应用优惠券
- self.applyCoupon = function(couponCode) {
- var deferred = $q.defer();
-
- // 这里应该有一个验证优惠券的逻辑
- // 为了演示,我们假设有一个固定的优惠券
- if (couponCode === 'DISCOUNT10') {
- // 10%折扣
- var discount = self.getTotal() * 0.1;
- deferred.resolve({
- code: couponCode,
- type: 'percentage',
- value: 10,
- amount: discount
- });
- } else if (couponCode === 'SAVE50') {
- // 固定折扣50元
- deferred.resolve({
- code: couponCode,
- type: 'fixed',
- value: 50,
- amount: 50
- });
- } else {
- deferred.reject('无效的优惠券代码');
- }
-
- return deferred.promise;
- };
-
- // 初始化购物车
- self.initCart();
- }]);
复制代码
4.2 创建购物车控制器
在js/controllers/cartController.js中,我们创建一个控制器来管理购物车视图:
- // 购物车控制器
- app.controller('CartController', ['CartService', '$scope', '$timeout', function(CartService, $scope, $timeout) {
- var self = this;
-
- // 初始化变量
- self.cartItems = [];
- self.itemCount = 0;
- self.total = 0;
- self.loading = false;
- self.message = null;
- self.messageType = null;
- self.couponCode = '';
- self.discount = null;
- self.finalTotal = 0;
-
- // 加载购物车数据
- self.loadCart = function() {
- self.cartItems = CartService.getCartItems();
- self.itemCount = CartService.getItemCount();
- self.total = CartService.getTotal();
- self.calculateFinalTotal();
- };
-
- // 计算最终总价(应用折扣后)
- self.calculateFinalTotal = function() {
- if (self.discount) {
- self.finalTotal = self.total - self.discount.amount;
-
- // 确保最终价格不低于0
- if (self.finalTotal < 0) {
- self.finalTotal = 0;
- }
- } else {
- self.finalTotal = self.total;
- }
- };
-
- // 从购物车中移除商品
- self.removeItem = function(productId) {
- self.loading = true;
-
- CartService.removeFromCart(productId)
- .then(function(response) {
- self.loadCart();
- self.showMessage(response.message, 'success');
- })
- .catch(function(error) {
- self.showMessage('移除失败: ' + error, 'error');
- })
- .finally(function() {
- self.loading = false;
- });
- };
-
- // 更新商品数量
- self.updateQuantity = function(productId, newQuantity) {
- // 防抖动,避免频繁请求
- if (self.updateTimeout) {
- $timeout.cancel(self.updateTimeout);
- }
-
- self.updateTimeout = $timeout(function() {
- self.loading = true;
-
- CartService.updateQuantity(productId, newQuantity)
- .then(function(response) {
- self.loadCart();
- self.showMessage(response.message, 'success');
- })
- .catch(function(error) {
- self.showMessage('更新失败: ' + error, 'error');
- // 恢复原来的数量
- self.loadCart();
- })
- .finally(function() {
- self.loading = false;
- });
- }, 500); // 500ms延迟
- };
-
- // 清空购物车
- self.clearCart = function() {
- if (confirm('确定要清空购物车吗?')) {
- self.loading = true;
-
- CartService.clearCart()
- .then(function(response) {
- self.loadCart();
- self.showMessage(response.message, 'success');
- })
- .catch(function(error) {
- self.showMessage('清空失败: ' + error, 'error');
- })
- .finally(function() {
- self.loading = false;
- });
- }
- };
-
- // 应用优惠券
- self.applyCoupon = function() {
- if (!self.couponCode) {
- self.showMessage('请输入优惠券代码', 'error');
- return;
- }
-
- self.loading = true;
-
- CartService.applyCoupon(self.couponCode)
- .then(function(discount) {
- self.discount = discount;
- self.calculateFinalTotal();
- self.showMessage('优惠券已应用', 'success');
- })
- .catch(function(error) {
- self.showMessage(error, 'error');
- self.discount = null;
- self.calculateFinalTotal();
- })
- .finally(function() {
- self.loading = false;
- });
- };
-
- // 移除优惠券
- self.removeCoupon = function() {
- self.discount = null;
- self.couponCode = '';
- self.calculateFinalTotal();
- self.showMessage('优惠券已移除', 'success');
- };
-
- // 结账
- self.checkout = function() {
- if (self.cartItems.length === 0) {
- self.showMessage('购物车是空的,无法结账', 'error');
- return;
- }
-
- // 这里应该有一个结账的逻辑
- // 为了演示,我们只是显示一个消息
- self.showMessage('结账功能尚未实现', 'info');
- };
-
- // 显示消息
- self.showMessage = function(message, type) {
- self.message = message;
- self.messageType = type;
-
- // 3秒后清除消息
- $timeout(function() {
- self.message = null;
- self.messageType = null;
- }, 3000);
- };
-
- // 监听购物车更新事件
- $scope.$on('cartUpdated', function(event, data) {
- self.itemCount = data.count;
- self.total = data.total;
- self.calculateFinalTotal();
- });
-
- // 初始化
- self.loadCart();
- }]);
复制代码
4.3 更新购物车视图
现在,我们更新index.html中的购物车区域:
- <!-- 购物车区域 -->
- <div ng-controller="CartController as cartCtrl">
- <h2>购物车 ({{cartCtrl.itemCount}} 件商品)</h2>
-
- <!-- 加载状态 -->
- <div ng-if="cartCtrl.loading" class="loading">
- <p>正在处理...</p>
- </div>
-
- <!-- 消息提示 -->
- <div ng-if="cartCtrl.message" class="message {{cartCtrl.messageType}}">
- <p>{{cartCtrl.message}}</p>
- </div>
-
- <!-- 购物车为空 -->
- <div ng-if="cartCtrl.cartItems.length === 0 && !cartCtrl.loading" class="empty-cart">
- <p>购物车是空的,去<a href="#/products">添加一些商品</a>吧!</p>
- </div>
-
- <!-- 购物车内容 -->
- <div ng-if="cartCtrl.cartItems.length > 0" class="cart-content">
- <!-- 购物车商品列表 -->
- <div class="cart-items">
- <div class="cart-item" ng-repeat="item in cartCtrl.cartItems">
- <div class="cart-item-image">
- <img ng-src="{{item.product.image}}" alt="{{item.product.name}}" onerror="this.src='images/placeholder.jpg'">
- </div>
- <div class="cart-item-info">
- <h3 class="cart-item-name">{{item.product.name}}</h3>
- <p class="cart-item-description">{{item.product.description}}</p>
- <div class="cart-item-price">
- ¥{{item.product.price | number:2}}
- </div>
- </div>
- <div class="cart-item-quantity">
- <label>数量:</label>
- <input type="number" min="1" max="{{item.product.stock}}"
- ng-model="item.quantity"
- ng-change="cartCtrl.updateQuantity(item.product.id, item.quantity)">
- <span class="stock-info">库存: {{item.product.stock}}</span>
- </div>
- <div class="cart-item-total">
- ¥{{(item.product.price * item.quantity) | number:2}}
- </div>
- <div class="cart-item-actions">
- <button class="remove-btn" ng-click="cartCtrl.removeItem(item.product.id)">
- 移除
- </button>
- </div>
- </div>
- </div>
-
- <!-- 购物车摘要 -->
- <div class="cart-summary">
- <div class="summary-row">
- <span>商品总数:</span>
- <span>{{cartCtrl.itemCount}} 件</span>
- </div>
- <div class="summary-row">
- <span>商品总价:</span>
- <span>¥{{cartCtrl.total | number:2}}</span>
- </div>
-
- <!-- 优惠券部分 -->
- <div class="coupon-section">
- <div class="summary-row" ng-if="!cartCtrl.discount">
- <input type="text" ng-model="cartCtrl.couponCode" placeholder="输入优惠券代码">
- <button ng-click="cartCtrl.applyCoupon()">应用</button>
- </div>
- <div class="summary-row" ng-if="cartCtrl.discount">
- <span>优惠券 ({{cartCtrl.discount.code}}):</span>
- <span>-¥{{cartCtrl.discount.amount | number:2}}</span>
- <button class="remove-coupon-btn" ng-click="cartCtrl.removeCoupon()">移除</button>
- </div>
- </div>
-
- <div class="summary-row total">
- <span>总计:</span>
- <span>¥{{cartCtrl.finalTotal | number:2}}</span>
- </div>
-
- <div class="cart-actions">
- <button class="clear-cart-btn" ng-click="cartCtrl.clearCart()">
- 清空购物车
- </button>
- <button class="checkout-btn" ng-click="cartCtrl.checkout()">
- 结账
- </button>
- </div>
- </div>
- </div>
- </div>
复制代码
4.4 添加购物车样式
在css/style.css中添加购物车相关样式:
现在,我们已经完成了购物车的基本功能。用户可以将商品添加到购物车,更新商品数量,移除商品,应用优惠券,以及查看总价。接下来,我们将实现一些高级功能。
5. 高级功能实现
5.1 本地存储与持久化
我们已经在购物车服务中实现了本地存储功能,使用localStorage来保存购物车数据。这样即使用户刷新页面或关闭浏览器,购物车数据也不会丢失。
5.2 优惠券系统
我们已经实现了一个简单的优惠券系统,支持两种类型的优惠券:
• 百分比折扣(例如:DISCOUNT10 提供10%的折扣)
• 固定金额折扣(例如:SAVE50 提供50元的折扣)
5.3 购物车动画效果
让我们为购物车添加一些动画效果,使用户体验更加流畅。首先,我们需要在HTML中引入AngularJS的动画模块:
- <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular-animate.min.js"></script>
复制代码
然后,在js/app.js中添加动画模块依赖:
- var app = angular.module('shoppingCartApp', ['ngAnimate']);
复制代码
现在,我们可以为购物车添加一些动画效果。在css/style.css中添加:
- /* 动画效果 */
- .cart-item.ng-enter, .cart-item.ng-leave {
- transition: all 0.5s ease;
- }
- .cart-item.ng-enter, .cart-item.ng-leave.ng-leave-active {
- opacity: 0;
- height: 0;
- margin: 0;
- padding: 0;
- }
- .cart-item.ng-leave, .cart-item.ng-enter.ng-enter-active {
- opacity: 1;
- height: auto;
- margin: inherit;
- padding: inherit;
- }
- .product-card.ng-enter {
- animation: fadeIn 0.5s;
- }
- @keyframes fadeIn {
- from {
- opacity: 0;
- transform: translateY(20px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
- }
- .message.ng-enter {
- animation: slideIn 0.3s;
- }
- @keyframes slideIn {
- from {
- transform: translateY(-20px);
- opacity: 0;
- }
- to {
- transform: translateY(0);
- opacity: 1;
- }
- }
复制代码
5.4 购物车实时更新
我们已经实现了购物车的实时更新,通过使用AngularJS的事件系统($broadcast和$on)来通知控制器购物车状态的变化。这样,当用户在商品页面添加商品到购物车时,购物车区域会自动更新。
5.5 购物车验证
我们已经在购物车服务中添加了一些验证逻辑,例如:
• 检查商品库存
• 验证商品数量
• 验证优惠券代码
5.6 购物车响应式设计
我们的购物车已经具有响应式设计,可以适应不同的屏幕尺寸。在css/style.css中,我们可以添加一些媒体查询来进一步优化移动设备上的显示效果:
- /* 响应式设计 */
- @media (max-width: 768px) {
- .content {
- flex-direction: column;
- }
-
- .product-list {
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
- }
-
- .cart-item {
- flex-wrap: wrap;
- }
-
- .cart-item-image {
- width: 100%;
- height: 200px;
- margin-right: 0;
- margin-bottom: 10px;
- }
-
- .cart-item-info {
- width: 100%;
- margin-right: 0;
- margin-bottom: 10px;
- }
-
- .cart-item-quantity {
- width: 100%;
- margin-right: 0;
- margin-bottom: 10px;
- }
-
- .cart-item-total {
- width: 100%;
- margin-right: 0;
- margin-bottom: 10px;
- text-align: left;
- }
-
- .cart-item-actions {
- width: 100%;
- }
-
- .cart-summary {
- width: 100%;
- }
- }
- @media (max-width: 480px) {
- .product-list {
- grid-template-columns: 1fr;
- }
-
- .cart-summary {
- padding: 15px;
- }
-
- .coupon-section input {
- width: 100%;
- margin-bottom: 5px;
- }
-
- .cart-actions {
- flex-direction: column;
- gap: 10px;
- }
-
- .clear-cart-btn, .checkout-btn {
- width: 100%;
- }
- }
复制代码
6. 性能优化
6.1 防抖动处理
在购物车控制器中,我们已经实现了更新商品数量的防抖动处理,避免用户快速修改数量时发送过多请求:
- // 防抖动,避免频繁请求
- if (self.updateTimeout) {
- $timeout.cancel(self.updateTimeout);
- }
- self.updateTimeout = $timeout(function() {
- // 更新逻辑
- }, 500); // 500ms延迟
复制代码
6.2 懒加载
我们可以实现商品的懒加载,提高页面加载性能。修改ProductService中的getProducts方法:
- // 获取所有商品(支持分页)
- self.getProducts = function(page, limit) {
- var deferred = $q.defer();
-
- page = page || 1;
- limit = limit || 10;
-
- $http.get('data/products.json')
- .then(function(response) {
- var products = response.data;
- var startIndex = (page - 1) * limit;
- var endIndex = startIndex + limit;
- var paginatedProducts = products.slice(startIndex, endIndex);
-
- deferred.resolve({
- products: paginatedProducts,
- total: products.length,
- page: page,
- totalPages: Math.ceil(products.length / limit)
- });
- })
- .catch(function(error) {
- deferred.reject(error);
- console.error('获取商品数据失败:', error);
- });
-
- return deferred.promise;
- };
复制代码
然后,在ProductController中添加分页逻辑:
- // 分页变量
- self.pagination = {
- page: 1,
- limit: 6,
- totalPages: 0,
- totalItems: 0
- };
- // 加载商品数据(支持分页)
- self.loadProducts = function() {
- self.loading = true;
- self.error = null;
-
- ProductService.getProducts(self.pagination.page, self.pagination.limit)
- .then(function(response) {
- self.products = response.products;
- self.pagination.totalItems = response.total;
- self.pagination.totalPages = response.totalPages;
- self.pagination.page = response.page;
- self.extractCategories();
- self.loading = false;
- })
- .catch(function(error) {
- self.error = '加载商品失败: ' + error;
- self.loading = false;
- });
- };
- // 上一页
- self.prevPage = function() {
- if (self.pagination.page > 1) {
- self.pagination.page--;
- self.loadProducts();
- // 滚动到顶部
- window.scrollTo(0, 0);
- }
- };
- // 下一页
- self.nextPage = function() {
- if (self.pagination.page < self.pagination.totalPages) {
- self.pagination.page++;
- self.loadProducts();
- // 滚动到顶部
- window.scrollTo(0, 0);
- }
- };
- // 跳转到指定页
- self.goToPage = function(page) {
- if (page >= 1 && page <= self.pagination.totalPages && page !== self.pagination.page) {
- self.pagination.page = page;
- self.loadProducts();
- // 滚动到顶部
- window.scrollTo(0, 0);
- }
- };
复制代码
在商品列表视图中添加分页控件:
- <!-- 分页控件 -->
- <div ng-if="productCtrl.pagination.totalPages > 1" class="pagination">
- <button ng-click="productCtrl.prevPage()"
- ng-disabled="productCtrl.pagination.page === 1">
- 上一页
- </button>
-
- <span ng-repeat="page in [].constructor(productCtrl.pagination.totalPages) track by $index">
- <button ng-class="{active: productCtrl.pagination.page === $index + 1}"
- ng-click="productCtrl.goToPage($index + 1)">
- {{$index + 1}}
- </button>
- </span>
-
- <button ng-click="productCtrl.nextPage()"
- ng-disabled="productCtrl.pagination.page === productCtrl.pagination.totalPages">
- 下一页
- </button>
- </div>
复制代码
添加分页控件的样式:
- /* 分页控件样式 */
- .pagination {
- display: flex;
- justify-content: center;
- margin-top: 20px;
- gap: 5px;
- }
- .pagination button {
- padding: 8px 12px;
- background-color: #ecf0f1;
- border: 1px solid #bdc3c7;
- border-radius: 4px;
- cursor: pointer;
- transition: all 0.3s;
- }
- .pagination button.active {
- background-color: #3498db;
- color: white;
- border-color: #3498db;
- }
- .pagination button:disabled {
- background-color: #f8f9fa;
- color: #6c757d;
- cursor: not-allowed;
- }
复制代码
6.3 图片优化
我们可以使用懒加载来优化图片加载,提高页面性能。首先,创建一个图片懒加载的指令:
- // 图片懒加载指令
- app.directive('lazyLoad', function() {
- return {
- restrict: 'A',
- link: function(scope, element, attrs) {
- var observer = new IntersectionObserver(function(entries) {
- entries.forEach(function(entry) {
- if (entry.isIntersecting) {
- element.attr('src', attrs.lazyLoad);
- observer.unobserve(element[0]);
- }
- });
- }, {
- rootMargin: '50px'
- });
-
- observer.observe(element[0]);
-
- // 当元素被销毁时,停止观察
- scope.$on('$destroy', function() {
- observer.unobserve(element[0]);
- });
- }
- };
- });
复制代码
然后,在商品卡片模板中使用这个指令:
- <div class="product-image">
- <img lazy-load="{{product.image}}" alt="{{product.name}}" src="images/placeholder.jpg">
- </div>
复制代码
7. 完整项目整合
现在,让我们整合所有功能,创建一个完整的购物车应用。以下是最终的index.html文件:
8. 总结与展望
8.1 项目总结
在本教程中,我们使用AngularJS构建了一个完整的购物车应用,实现了以下功能:
1. 商品展示:展示商品列表,支持按类别筛选和分页。
2. 购物车管理:添加商品到购物车,更新商品数量,移除商品,清空购物车。
3. 优惠券系统:支持百分比折扣和固定金额折扣。
4. 数据持久化:使用localStorage保存购物车数据。
5. 动画效果:添加了商品添加/移除的动画效果。
6. 性能优化:实现了防抖动处理、懒加载和分页功能。
7. 响应式设计:适应不同屏幕尺寸的设备。
8.2 技术要点
通过这个项目,我们深入理解了AngularJS的核心概念和最佳实践:
1. 数据绑定:使用AngularJS的双向数据绑定,实现了模型和视图的自动同步。
2. 依赖注入:使用AngularJS的依赖注入系统,管理应用中的服务和控制器。
3. 自定义指令:创建了可复用的自定义指令,如商品卡片和图片懒加载。
4. 服务:使用服务封装业务逻辑,实现代码的模块化和可维护性。
5. 事件系统:使用AngularJS的事件系统,实现组件间的通信。
6. 动画:使用AngularJS的动画模块,提升用户体验。
8.3 未来展望
虽然这个购物车应用已经功能完善,但还有一些可以进一步优化的方向:
1. 后端集成:目前我们使用的是静态JSON数据,可以集成真实后端API,实现完整的电商功能。
2. 用户认证:添加用户登录和注册功能,实现用户专属购物车。
3. 支付集成:集成支付网关,实现完整的结账流程。
4. 订单管理:添加订单历史和状态跟踪功能。
5. 搜索功能:实现商品搜索和高级筛选。
6. 评价系统:添加商品评价和评分功能。
7. 推荐系统:基于用户行为实现商品推荐。
8. 国际化:支持多语言和货币。
8.4 AngularJS与现代框架
虽然AngularJS是一个强大的框架,但需要注意的是,AngularJS(1.x版本)已经进入了长期支持阶段,不再有新功能开发。对于新项目,可以考虑使用现代框架如Angular(2+)、React或Vue.js。
不过,AngularJS的许多核心概念,如数据绑定、依赖注入、服务和指令等,在现代框架中仍然有对应的概念。因此,通过学习AngularJS,可以为学习现代前端框架打下坚实的基础。
9. 结语
本教程通过一个完整的购物车项目,详细介绍了AngularJS的数据绑定与状态管理,以及如何构建专业的电商应用。我们从基础到高级,逐步实现了购物车的各项功能,并进行了性能优化和用户体验提升。
希望这个教程能够帮助你深入理解AngularJS的核心概念和最佳实践,为你的前端开发之路提供有力的支持。如果你有任何问题或建议,欢迎随时交流。祝你学习愉快! |
|