如何观看集合中的每个对象?

时间:2014-05-08 09:43:29

标签: angularjs watch

在Angular范围内,我有一组带有一些数据的对象,加上xy坐标。必须根据xy值重新计算其他一些范围变量。有效地做到这一点的最佳方法是什么?

  • 如果我使用$scope.$watch(collection, handler)$scope.$watchCollection(collection, handler),我就不会收到有关其所包含对象的更改的通知。
  • 如果我使用$scope.$watch(collection, handler, true),我会收到通知,但是当发生任何变化时,不仅xy。另外,我不知道该系列的哪个元素发生了变化(我认为这种深度比较相当昂贵)。

理想情况下,我想编写类似$scope.$watchObjects(collection, ["x", "y"], handler)的内容,其中我的处理程序将使用更改的对象以及可能的索引进行调用。有没有一种简单的方法可以做到这一点?

4 个答案:

答案 0 :(得分:1)

你能做到:

angular.forEach(colletion, function(object) {
    $scope.$watch(object, function() {
        ... I'm not sure what would you like to do with object here...
    })

}, true)

答案 1 :(得分:1)

我很确定它出现在这段视频中:https://www.youtube.com/watch?v=zyYpHIOrk_Y 但在某个地方,我看到Angular开发人员正在谈论将您正在观看的数据映射到一个较小的子集,可能是这样的:

$scope.$watchCollection(function() {
    return yourList.map(function(listItem) {
        return { 'x': listItem.x, 'y': listItem.y };
    };
}, function(newVal, oldVal) {
    // perform calculations
});

这会让你只看一个具有x和y属性的对象数组。

答案 2 :(得分:1)

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

请记住,必须在$ scope上声明该集合。

答案 3 :(得分:0)

基于Slaven Tomac’s answer,这是我想出的。基本上:它使用$watchCollection来检测何时在集合中插入或添加项目。对于每个添加的项目,它开始监视它。对于每个已删除的项目,它会停止监视它。然后,每当对象发生更改时,它都会通知侦听器。

这进一步允许细化应该被视为对象本身的变化或仅仅是集合中的变化。 sameId函数用于测试两个对象ab是否应该被认为是相同的(它可能只是a === b,但它可能更复杂一些 - 特别是,如果您将字段名称作为sameId参数传递[例如"id"],则两个对象将被视为“相同”。)

createArrayDiffs改编自Eclipse Modeling Framework中的类似变更检测方法,并且本身很有趣:它返回一个数组与另一个数组之间发生的变化列表。这些更改是插入,删除和对象更改(根据传递的字段)。

样本用法:

watchObjectsIn($rootScope, "activities", "id", ["x", "y"], function (oldValue, newValue) {
    console.log("Value of an object changed: from ", oldValue, " to ", newValue);
});

当然,我对任何更简单和/或更有效的解决方案感兴趣!

实现(编译的TypeScript):

function watchObjectsIn(scope, expr, idField, watchedFields, listener) {
    var fieldCompareFunction = makeFieldCompareFunction(watchedFields);
    var unbindFunctions = [];
    function doWatch(elem, i) {
        var unbindFunction = scope.$watch(function () {
            return elem;
        }, function (newValue, oldValue) {
            if (newValue === oldValue)
                return;
            if (!fieldCompareFunction(oldValue, newValue))
                listener(oldValue, newValue);
        }, true);
        unbindFunctions.push(unbindFunction);
    }
    function unwatch(elem, i) {
        unbindFunctions[i]();
        unbindFunctions.splice(i, 1);
    }
    scope.$watchCollection(expr, function (newArray, oldArray) {
        if (isUndef(newArray))
            return;

        var diffs = createArrayDiffs(oldArray, newArray, idField, fieldCompareFunction);
        if (diffs.length === 0 && newArray.length !== unbindFunctions.length) {
                for (var i = unbindFunctions.length - 1; i >= 0; i--) {
                unwatch(null, 0);
            }
            diffs = createArrayDiffs([], newArray, idField);
        }

        _.forEach(diffs, function (diff) {
            switch (diff.changeType()) {
                case 0 /* Addition */:
                    doWatch(diff.newValue, diff.position);
                    break;
                case 1 /* Removal */:
                    unwatch(diff.oldValue, diff.position);
                    break;
                case 2 /* Change */:
                    listener(diff.oldValue, diff.newValue);
                    break;
            }
        });
    });
}

function isUndef(v) {
    return typeof v === "undefined";
}
function isDef(v) {
    return typeof v !== "undefined";
}

function parseIntWithDefault(str, deflt) {
    if (typeof deflt === "undefined") { deflt = 0; }
    var res = parseInt(str, 10);
    return isNaN(res) ? deflt : res;
}

function cssIntOr0(query, cssProp) {
    return parseIntWithDefault(query.css(cssProp));
}

function randomStringId() {
    return Math.random().toString(36).substr(2, 9);
}

var ArrayDiffChangeType;
(function (ArrayDiffChangeType) {
    ArrayDiffChangeType[ArrayDiffChangeType["Addition"] = 0] = "Addition";
    ArrayDiffChangeType[ArrayDiffChangeType["Removal"] = 1] = "Removal";
    ArrayDiffChangeType[ArrayDiffChangeType["Change"] = 2] = "Change";
})(ArrayDiffChangeType || (ArrayDiffChangeType = {}));

var ArrayDiffEntry = (function () {
    function ArrayDiffEntry(position, oldValue, newValue) {
        this.position = position;
        this.oldValue = oldValue;
        this.newValue = newValue;
    }
    ArrayDiffEntry.prototype.changeType = function () {
        if (isUndef(this.oldValue))
            return 0 /* Addition */;
        if (isUndef(this.newValue))
            return 1 /* Removal */;
        return 2 /* Change */;
    };

    return ArrayDiffEntry;
})();

function makeFieldCompareFunction(fields) {
    return function (o1, o2) {
        for (var i = 0; i < fields.length; i++) {
            var fieldName = fields[i];
            if (o1[fieldName] !== o2[fieldName])
                return false;
        }
        return true;
    };
}

function createArrayDiffs(oldArray, newArray, sameId, sameData, undefined) {
    if (isUndef(sameId)) {
        sameId = angular.equals;
    } else if (_.isString(sameId)) {
        var idFieldName = sameId;
        sameId = function (o1, o2) {
            return o1[idFieldName] === o2[idFieldName];
        };
    }

    var doDataChangedCheck = isDef(sameData);
    if (doDataChangedCheck && !_.isFunction(sameData)) {
        if (_.isString(sameData))
            sameData = [sameData];
        var fieldsToCheck = sameData;
        sameData = makeFieldCompareFunction(fieldsToCheck);
    }

    var arrayDiffs = [];

    function arrayIndexOf(array, element, index) {
        for (var i = index; i < array.length; i++) {
            if (sameId(array[i], element))
                return i;
        }
        return -1;
    }

    var oldArrayCopy = oldArray ? oldArray.slice() : [];

    var index = 0;
    var i;
    for (i = 0; i < newArray.length; i++) {
        var newValue = newArray[i];
        if (oldArrayCopy.length <= index) {
            arrayDiffs.push(new ArrayDiffEntry(index, undefined, newValue));
        } else {
            var done;
            do {
                done = true;
                var oldValue = oldArrayCopy[index];
                if (!sameId(oldValue, newValue)) {
                    var oldIndexOfNewValue = arrayIndexOf(oldArrayCopy, newValue, index);
                    if (oldIndexOfNewValue !== -1) {
                        var newIndexOfOldValue = arrayIndexOf(newArray, oldValue, index);
                        if (newIndexOfOldValue === -1) {
                            arrayDiffs.push(new ArrayDiffEntry(index, oldValue, undefined));
                            oldArrayCopy.splice(index, 1);
                            done = false;
                        } else if (newIndexOfOldValue > oldIndexOfNewValue) {
                            if (oldArrayCopy.length <= newIndexOfOldValue) {
                                newIndexOfOldValue = oldArrayCopy.length - 1;
                            }
                            arrayDiffs.push(new ArrayDiffEntry(index, oldValue, undefined));
                            oldArrayCopy.splice(index, 1);
                            arrayDiffs.push(new ArrayDiffEntry(newIndexOfOldValue, undefined, oldValue));
                            oldArrayCopy.splice(newIndexOfOldValue, 0, oldValue);
                            done = false;
                        } else {
                            arrayDiffs.push(new ArrayDiffEntry(oldIndexOfNewValue, newValue, undefined));
                            oldArrayCopy.splice(oldIndexOfNewValue, 1);
                            arrayDiffs.push(new ArrayDiffEntry(index, undefined, newValue));
                            oldArrayCopy.splice(index, 0, newValue);
                        }
                    } else {
                        oldArrayCopy.splice(index, 0, newValue);
                        arrayDiffs.push(new ArrayDiffEntry(index, undefined, newValue));
                    }
                } else {
                    if (doDataChangedCheck && !sameData(oldValue, newValue)) {
                        arrayDiffs.push(new ArrayDiffEntry(i, oldValue, newValue));
                    }
                }
            } while(!done);
        }
        index++;
    }
    for (i = oldArrayCopy.length; i > index;) {
        arrayDiffs.push(new ArrayDiffEntry(--i, oldArrayCopy[i], undefined));
    }

    return arrayDiffs;
}