【问题标题】:Would $watch be of any use here?$watch 在这里有用吗?
【发布时间】:2014-06-18 15:31:28
【问题描述】:

我正在使用 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/

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

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

现在在指令中,函数 $scope.subTotalInvoker() 调用 ajax 调用。来自该回调的回调设置了“subTotal”的值

再一次,这工作得很好。

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

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

我尝试将 $watch 添加到 $scope.icRows 但它不起作用。也许我做错了什么。

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

谢谢

约翰

【问题讨论】:

    标签: angularjs angularjs-directive


    【解决方案1】:

    解决方案 1 - 使用 $watchCollection

    您可以使用 $watchCollection。当整个数组在作用域上被替换时(即 $scope.icRows = []),当从 icRows 添加或删除项目时,或者替换项目之一时,$watchCollection 将调用其监视处理程序。

    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 进一步将结果格式化为货币。

    【讨论】:

    • 感谢 Pixelbits。您的答案的 $watchCollection 部分工作正常,但正如您所建议的,它不适用于手头的示例,其中行内的值被更新(即在 jsfiddle 中输入价格字段):jsfiddle.net/jralston/edkhf 第二部分使用不幸的是,过滤器似乎不适用,因为它不考虑 ajax 回调。不过谢谢,非常有用的答案。
    • 过滤器应该可以产生你需要的结果。你怎么称呼ajax?你在使用 $http 服务吗?
    • 啊,我没有看到你答案的中间部分(你编辑了吗?),用 $watch('icRows,function(newVal){},true)。效果很好。演示在这里:[链接]jsfiddle.net/jralston/Vr63f/[/link]非常感谢
    • 重新 ajax。它是在问题的第二个 jsFiddle 中使用 setTimeout 函数模拟的。这就是问题的根源,因为正在发生两件事。 1. 对 icRows 数组的更改正在调用事件 2. 来自此 ajax 的回调正在更新变量。我没有使用 $http 服务,也没有能力这样做。谢谢一百万。
    • 当然没问题。我很高兴能帮上忙:)
    【解决方案2】:

    您不需要指令或过滤器。只需在控制器中添加 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

    【讨论】:

    • +1 用于观察不需要过滤器或指令,但这个答案并不考虑 ajax 回调。 IE。对 icRows 集合的更改会触发 ajax 调用,回调将更新手头的变量。
    猜你喜欢
    • 2018-05-25
    • 2014-08-21
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 2016-04-16
    • 2018-09-13
    相关资源
    最近更新 更多