$ watch会在这里有用吗?

时间:2014-06-18 15:31:28

标签: angularjs angularjs-directive

我使用angularjs指令模板从传入的模型中渲染单个计算值(总和)。

如果我使用模板正常渲染值,则没有问题。 渲染的值只是传递给指令/模板的一堆行对象值的总和,如下所示:

ngApp.directive('subtotal', function() {
    return {
        restrict: 'E',
        scope: { 
            icRows: '='
        },
        template: '<div><strong>Sub Total:</strong> {{subTotal()}}</div>',
        replace: true,
        link: function($scope, $element, $attr) {
            $scope.subTotal = function() {
                var t = 0;
                for(var i=0; i<$scope.icRows.length; i++ ) {
                    t += $scope.icRows[i].price;
                }
                return t;
            }
        }
    };
});


<div ng-app="app">

    <div ng-controller="MyCtrl">
        <div ng-repeat="row in rows">
            <input ng-model="row.name" /> | <input ng-model="row.price" />
        </div>
        <hr/>
        <subtotal ic-rows="rows"></subtotal>
    </div>
</div>

这很好用。 在这里演示:http://jsfiddle.net/jralston/p8FLV/

我的问题是&#39; subTotal&#39;变量实际上是AJAX调用的结果。当更改“&#39;行”时,需要调用此ajax调用。变量发生。为此,我已将指令中的模板调整为:

<div><strong>Sub Total:</strong> {{subTotalInvoker()}} {{subTotal}}</div>

现在在指令中,函数$ scope.subTotalInvoker()调用ajax调用。来自的回调设置了&#39; subTotal&#39;

的值

同样,这完全正常。

在这里演示: http://jsfiddle.net/jralston/wpYjL/

然而,这似乎是一种完全错误的做法。

我尝试在$ scope.icRows中添加$ watch,但它没有用。也许我做错了什么。

非常欢迎任何有关如何做到这一点的指示和/或更正。

由于

约翰

2 个答案:

答案 0 :(得分:2)

解决方案1 ​​ - 使用$ watchCollection

您可以使用$ watchCollection。 $ watchCollection将在范围内替换整个数组时调用其监视处理程序(即$ scope.icRows = []),在icRows中添加或删除项目时,或替换其中一个项目。

ngApp.directive('subtotal', function() {
    return {
        restrict: 'E',
        scope: { 
            icRows: '='
        },
        template: '<div><strong>Sub Total:</strong> {{subTotal}}</div>',
        replace: true,
        link: function($scope, $element, $attr) {
            $scope.subTotal = 0;
            $scope.$watchCollection('icRows', function(newVal) {
                var t = 0;
                for(var i=0; i<newVal.length; i++ ) {
                    t += newVal[i].price;
                }
                $scope.subTotal = t;
            }

        }
    };
});

解决方案2 - 使用Deep $ watch

但我怀疑这不合适,因为'。price'可能会改变,而$ watchCollection不会检查项目的属性是否有变化。

如果您需要深度监视(搜索属性和子属性的更改),则使用$ watch并将true作为第二个参数传递:

$scope.$watch('icRows', function(newVal) {
     ...
}, true);

$ watch(true)具有您需要注意的细微差别。我不确定最近Angular的更新是否有所改变,但是当我使用它时,我发现$ watch(true)不检查数组的添加/删除,或者即使数组完全被替换。虽然它确实做了很深的观察。

过滤器 - 另类方法

过滤器的工作方式类似于$ watchCollection。缺点是当项目的.price属性更改时,过滤器不会更新。

ngApp.filter('subtotal', function() {
    return function(input) { 
         var t = 0; 
         for (var i= 0; i < input.length; ++i) {
             t += parseFloat(input[i].price);
         }
         return t;
    }
});

然后在HTML中,您可以将过滤器应用于范围内的数组:

<div ng-app="app">

    <div ng-controller="MyCtrl">
        <div ng-repeat="row in rows">
            <input ng-model="row.name" /> | <input ng-model="row.price" />
        </div>
        <hr/>
        {{ rows | subtotal | currency }}
    </div>
</div>

如果这符合您的要求,则无需自定义指令,您可以使用currency filter进一步将结果格式化为货币。

答案 1 :(得分:1)

您不需要指令或过滤器。只需在控制器中添加subTotal()功能:

$scope.subTotal = function(rows){
    var t = 0;
    for(var i=0; i<rows.length; i++ ) {
        t += rows[i].price;
    }
    return t;
}

并在HTML中绑定该函数:

<div><strong>Sub Total:</strong> {{subTotal(rows)}}</div>

Demo