如何在Angular组件中实现$scope.watch功能?
AngularJS组件式购物车总价不更新问题解决
我尝试用两种方式实现简单图书购物车:
- 模块+控制器方式:通过
$scope.watch监听图书数组,总价total能正常随图书增删改更新 - 模块+组件方式:初始化时总价显示正确,但增删改图书时
total无法自动更新
一、模块+控制器(正常运行)
HTML代码
<body ng-controller="CartControler" ng-init="updateTotal()"> <table> <caption><b>My Books</b></caption> <thead> <tr> <th>Title</th> <th>Qty</th> <th>$UnitPrice</th> <th>Line Total</th> <th>Total {{total | currency}}</th> <!-- 显示正确金额 --> </tr> <!-- 省略表格内容 --> </thead> </table> </body>
JS代码
angular.module('myApp', []) .controller('CartControler', function ($scope) { $scope.books = [ {title: 'Absolute Java', qty: 1, price: 114.95}, {title: 'Pro HTML5', qty: 2, price: 27.95}, {title: 'Head First HTML5', qty: 1, price: 27.89} ]; $scope.total; $scope.updateTotal = function(value) { $scope.total = 0; for(v in value){ $scope.total += value[v].price * value[v].qty; } return $scope.total; } // 深度监听books数组变化 $scope.$watch('books', function() { $scope.updateTotal($scope.books); }, true) })
二、模块+组件(总价不更新)
HTML代码
<table> <caption><b>My Books</b></caption> <thead> <tr> <th>Title</th> <th>Qty</th> <th>$UnitPrice</th> <th>Line Total</th> <th>Total {{$ctrl.total | currency}} </th> <!-- 初始化显示正确,但增删改图书时不更新 --> </tr> </thead> <tbody> <!-- 省略表格内容 --> </tbody> </table>
JS代码
angular.module("cartApp") .component('cartList', { templateUrl: 'cart-list/cart-list.template.html', init: 'updateTotal()', // 非标准选项,不生效 controller: function CartListController() { this.books = [ {title: 'Absolute Java', qty: 1, price: 114.95}, {title: 'Pro HTML5', qty: 2, price: 27.95}, {title: 'Head First HTML5', qty: 1, price: 27.89} ]; this.total; this.updateTotal = function(value) { this.total = 0; for(v in value){ this.total += value[v].price * value[v].qty; } return this.total; } // 仅初始化时计算一次总价 this.total = this.updateTotal(this.books); } })
问题原因及修复方案
问题原因
组件控制器仅在初始化时调用了updateTotal计算总价,没有监听books数组的变化,后续增删改图书时无法触发重新计算。另外,组件配置里的init不是AngularJS组件的标准选项,不会执行。
修复后的组件JS代码
angular.module("cartApp") .component('cartList', { templateUrl: 'cart-list/cart-list.template.html', // 注入$scope用于监听 controller: ['$scope', function CartListController($scope) { var ctrl = this; // 保存控制器上下文,避免回调中this指向变化 ctrl.books = [ {title: 'Absolute Java', qty: 1, price: 114.95}, {title: 'Pro HTML5', qty: 2, price: 27.95}, {title: 'Head First HTML5', qty: 1, price: 27.89} ]; ctrl.updateTotal = function(value) { ctrl.total = 0; // 声明局部变量v,避免污染全局作用域 for(var v in value){ ctrl.total += value[v].price * value[v].qty; } return ctrl.total; } // 深度监听books数组的变化(包括元素属性修改、增删) $scope.$watch(function() { return ctrl.books; }, function() { ctrl.updateTotal(ctrl.books); }, true); // 初始化计算总价 ctrl.total = ctrl.updateTotal(ctrl.books); }] })
关键说明
- 注入
$scope实现监听:AngularJS组件控制器可以注入$scope,通过$scope.$watch监听数据变化 - 深度监听:第三个参数
true会监听数组内每个元素的属性(如qty、price)变化,适合购物车场景;若只需监听数组增删,可去掉该参数提升性能 - 保存控制器上下文:用
var ctrl = this避免回调函数中this指向改变的问题 - 移除无效的
init选项:组件没有init配置,初始化逻辑直接写在控制器内
内容的提问来源于stack exchange,提问作者Munia11
相关产品推荐
相关产品推荐

