我有一个自定义的AngularJS组件,可以在单个网页上使用200多次。该页面最终实现了4000多名观察者-超过了AngularJS首选的最大观察者数量-并使该页面真正变慢。
实际的问题是,组件模板中的一些ng-if
和其他AngularJS表达式遗留了许多不需要的观察者,不再需要更改其值。
对于普通的ng-if
来说,修复很容易:
<div ng-if="::$ctrl.isInitialized()">Ready!</div>
......,其中$ctrl.isInitialized()
会返回true
(初始化组件时)或undefined
(直到它被初始化)。
在此处返回undefined
将使AngularJS保持观察者处于活动状态,直到它返回其他值(在这种情况下为值true
),然后将div添加到DOM中。
没有ng-not="expression"
,就像ng-hide
一样。这与ng-hide
搭配使用效果很好,除了在控制器初始化之后div仍然位于DOM中之外,这不是完美的解决方案。
但是如何实现它,以使<div>
在DOM中,直到控制器被初始化,然后将其删除?
答案 0 :(得分:0)
尽管没有ng-not
指令,但是很容易从AngularJS源代码中实现:
var ngNotDirective = ['$animate', '$compile', function($animate, $compile) {
function getBlockNodes(nodes) {
// TODO(perf): update `nodes` instead of creating a new object?
var node = nodes[0];
var endNode = nodes[nodes.length - 1];
var blockNodes;
for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
if (blockNodes || nodes[i] !== node) {
if (!blockNodes) {
blockNodes = jqLite(slice.call(nodes, 0, i));
}
blockNodes.push(node);
}
}
return blockNodes || nodes;
}
return {
multiElement: true,
transclude: 'element',
priority: 600,
terminal: true,
restrict: 'A',
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude) {
var block, childScope, previousElements;
$scope.$watch($attr.ngNot, function ngNotWatchAction(value) {
if (!value) {
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
clone[clone.length++] = $compile.$$createComment('end ngNot', $attr.ngNot);
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
block = {
clone: clone
};
$animate.enter(clone, $element.parent(), $element);
});
}
} else {
if (previousElements) {
previousElements.remove();
previousElements = null;
}
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
previousElements = getBlockNodes(block.clone);
$animate.leave(previousElements).done(function(response) {
if (response !== false) previousElements = null;
});
block = null;
}
}
});
}
};
}];
这与ng-if
相同,但已还原了if (!value)
检查。
它可以这样使用:
<div ng-not="::$ctrl.isInitialized() ? true : undefined">Loading...</div>
通过在console.log()
中添加$ctrl.isInitialized()
可以很容易地验证是否有无用的观察者-该函数将被调用几次,直到返回true
且观察者为已删除-以及div及其内部的所有内容。
答案 1 :(得分:0)
一种快速修补程序:我想v1.1.5之后的表达式中,角度允许三元运算符。
因此您可以进行以下操作:
<div ng-if="::$ctrl.isInitialized() === undefined? undefined: !$ctrl.isInitialized()">
据我所知,undefined
在角度表达式中没有特殊含义-在$scope
中它被视为另一个(尚未定义)变量。所以我必须明确地把它放在那里:
$scope = undefined;
替代选项是编写短助手:
function isDefined(val) {
return angular.isDefined(val) || undefined;
}
以后将其用作
ng-if="::isDefined($ctrl.isInitialized()) && !$ctrl.isInitialized()"
但是由于您说的地方太多了-请确保按照上面的代码制作自己的组件看起来更好