AngularJs的$ watch功能 - 没有回报?

时间:2014-01-26 08:10:10

标签: javascript angularjs

根据$ watch的文档:

  

每次调用watchExpression时都会调用$digest()   返回将要观看的值

如果是这样,为什么这段代码会起作用? (jsbin

 $scope.$watch(function() {
      var total = 0;
      for (var i = 0; i < $scope.items.length; i++) {
        total = total + $scope.items[i].price * $scope.items[i].quantity;
      }
      $scope.bill.total = total;
      $scope.bill.discount = total > 100 ? 10 : 0;
      $scope.bill.subtotal = total - $scope.bill.discount;
    });

我在这里看不到任何return

并且文档明确表示应该返回值 - 应该返回。

2 个答案:

答案 0 :(得分:2)

在每个digest调用watchwatch正在查看watchExpression(通常是变量; $watch()的第一个参数)是否已更改。在你的情况下,$watch()的第一个参数是一个函数,而不是一个变量(这实际上不是问题,而是你的问题的原因:))。因此,每次调用digest时,它都会执行watch并执行您的函数。这就是它起作用的原因。

编辑: 你的那个函数可以或者应该有一个return,它将返回一个变量,以便在执行return语句之前监视你的函数中的任何代码。

答案 1 :(得分:1)

更新了回答问题的答案:“如果$ scope。$ watch什么都不看(未定义),它怎么运行?”

以下是关于摘要周期和观察者如何在Angular中工作的一些帖子:

* http://www.benlesh.com/2013/08/angularjs-watch-digest-and-apply-oh-my.html
* http://onehungrymind.com/notes-on-angularjs-scope-life-cycle/

重要的是要记住,在每个摘要周期中,Angular将始终至少运行一次watchExpression 。但如果listener的值与前一次调用不同,它只会运行watchExpression函数。

以下是一个简单示例,您希望将scope.otherCount设置为与scope.count相同的值。

$scope.count = 1;
$scope.otherCount = 0;

$scope.$watch(function(){
    // This function is called multiple times every digest cycle.
    // We should return a value to let Angular know when the listener needs to be run.

    // If the return value of this function is different between two calls then the listener will run.

    console.log('watchExpression');

    // This is important for Angular to know that our watch value has changed.
    return $scope.count;

}, function(newValue){

    // When the value of `$scope.count` changes this listener will be run to update `$scope.otherCount`.

    // The reason this listner is called is because our `watchExpression` above will
    // have returned a different value.
    $scope.otherCount = $scope.count;

    console.log('count=', $scope.count);
    console.log('count=', $scope.otherCount);
});

将它与手表相比较,你应该看看发生了什么。


原始答案:

我的回答中有一些我试图解决的隐藏问题:

1 - “为什么这不是错误?”

您的示例是有效的JavaScript - 未明确返回值的函数的返回值为undefined

function a(){
    // Don't return anything.
}

a() === undefined;  // true

Angular会运行您的watchExpression函数,并且每次都会从中获取值undefined。就Angular而言,watchExpression的值永远不会改变。

2 - “为什么会起作用?”

每次运行$scope时都会更新watchExpression,以便您在视图中看到正确的值。

但是,Angular在每个摘要周期中多次运行watchExpression,这意味着$scope的更新频率超出预期。

3 - “应该如何改变?”

如果您使用 Angular 1.2 (或更高版本),那么您应该使用新的$scope.$watchCollection方法来观察数组中的值:

$scope.$watchCollection('items', function(){
    var total = 0;
    for (var i = 0; i < $scope.items.length; i++) {
        total = total + $scope.items[i].price * $scope.items[i].quantity;
    }
    $scope.bill.total = total;
    $scope.bill.discount = total > 100 ? 10 : 0;
    $scope.bill.subtotal = total - $scope.bill.discount;
});

这将监视$scope.items数组,并在数组或数组中的项发生更改时运行侦听器函数。


如果您使用的是早期版本的角度(例如 Angular 1.0.x ),那么您需要找到一种不同的方式来触发重新计算:

<强> HTML:

在输入字段中添加ng-change事件,并在其值发生更改时重新计算:

...
<div ng-repeat="item in items">
    <span>{{item.title}}</span>
    <!-- Add ng-change="itemChanged()" to the input -->
    <input ng-model="item.quantity" ng-change="itemChanged()" />
    <span>{{item.price | currency}}</span>
    <span>{{item.price * item.quantity | currency}}</span>
</div>
...

<强> JavaScript的:

// Define a single function that is responsible for calculating the totals.
function calculateTotals(){
    var total = 0;
    for (var i = 0; i < $scope.items.length; i++) {
        total = total + $scope.items[i].price * $scope.items[i].quantity;
    }
    $scope.bill.total = total;
    $scope.bill.discount = total > 100 ? 10 : 0;
    $scope.bill.subtotal = total - $scope.bill.discount;
}

// Add a $watch that will calculate the totals when the array length changes.
$scope.$watch('items', calculateTotals);

// Recalculate the totals when the input value changes.
$scope.itemChanged = function(){
    calculateTotals();
};