[英]How to implement reverse one time bind ng-if expression in AngularJS?
我有一個自定義的AngularJS組件,可以在單個網頁上使用200多次。 該頁面最終實現了4000多名觀察者-這比AngularJS希望的最大觀察者數量更多-並使該頁面真正變慢。
實際的問題是,組件模板中的某些ng-if
和其他AngularJS表達式遺留了許多不需要的觀察者,它們不再需要更改其值。
對於普通的ng-if
,修復很容易:
<div ng-if="::$ctrl.isInitialized()">Ready!</div>
...其中$ctrl.isInitialized()
將返回true
(初始化組件時)或undefined
(直到它被undefined
)。
在這里返回undefined
將使AngularJS保持觀察者處於活動狀態,直到它返回其他值(在這種情況下為true
,然后將div添加到DOM中。
沒有ng-not="expression"
就像ng-hide
。 這對ng-hide
效果很好,除了在控制器初始化之后div仍然位於DOM中之外,這不是完美的解決方案。
但是,如何實現它,以使<div>
在DOM中一直存在,直到控制器已初始化並在之后將其刪除?
盡管沒有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>
通過在$ctrl.isInitialized()
添加console.log()
可以很容易地驗證是否存在無用的觀察程序-該函數將被調用幾次,直到返回true
並刪除觀察程序為止-以及div及其內部的任何內容。
一種快速修補程序:我猜想,角度允許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()"
但是,由於您說的地方太多了-請確保按照上面的代碼制作自己的組件看起來更好
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.