我正在读一本我遇到过以下代码的书(有一个名为product的2D数组有3行) -
HTML
<div unordered-list="products" list-property="price | currency"></div>
和JS
angular.module("exampleApp", [])
.directive("unorderedList", function () {
return function (scope, element, attrs) {
var data = scope[attrs["unorderedList"]];
var propertyExpression = attrs["listProperty"];
if (angular.isArray(data)) {
var listElem = angular.element("<ul>");
element.append(listElem);
for (var i = 0; i < data.length; i++) {
var itemElement = angular.element('<li>');
listElem.append(itemElement);
var watcherFn = function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
scope.$watch(watcherFn, function (newValue, oldValue) {
itemElement.text(newValue);
});
}
}
}
})
该书说,“AngularJS评估了三个观察者函数,它们在循环终止后引用数据[i]”。 “到时间循环终止时,i的值为3,这意味着所有三个观察者函数都试图访问 数据数组中不存在的对象,这就是指令不起作用的原因“。
观察者功能在循环内部并从范围调用。$ watch为什么会这样?
答案 0 :(得分:0)
这是由于javascript的变量范围有效。
您可以在JavaScript closure inside loops – simple practical example
上阅读完整的答案嗯,问题是每个匿名函数中的变量i都绑定到函数外的同一个变量。
同样适用于javascript观察者,这些观察者应用于不同的tick
。 tick表示下一个事件循环执行。
要了解更多相关信息,请阅读有关角度消化周期的更多信息。
因此,在执行函数之前,变量的值已经改变。
特定于角度的简单解决方案是为$watch
构造字符串表达式。
所以$scope.$watch('products[1].price | currency', ..)
应该有效。
根据您的代码段将其翻译为变量
$scope.$watch(`${attrs['unorderdList']}[${i}].${attrs['listProperty']}`, function(newValue){ ... })
或类似的东西应该有用。我需要更多信息来提供一个运行示例。
另一种不是针对角度的解决方案是将其绑定到不同的范围。所以如果循环的第一行是
for (let i = 0; i < data.length; i++) {
只需将var
替换为let
- 它就可以了。
此解决方案利用了新的JavaScript let
语法,该语法具有块范围而非函数范围。
for (let i = 0; i < data.length; i++) {
var itemElement = angular.element('<li>');
listElem.append(itemElement);
var watcherFn = function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
scope.$watch(watcherFn, function (newValue, oldValue) {
itemElement.text(newValue);
});
}
答案 1 :(得分:0)
这是因为这些函数将与闭包一起使用,闭包表示执行时的外部JS函数作用域的状态。这就是为什么在循环停止后变量i
将引用最后指定的值:3。
要使变量i
等于迭代时刻的值,您需要创建通过附加函数包装它的中间范围:
var watcherFn = (function(i)
return function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
}(i));
在这个答案中,您可以阅读JS中有关闭包的更多信息: How do JavaScript closures work?