我们有这个指令允许开发人员使用$ compile指定他们自己的模板和数据,这些模板和数据将用于将绑定到该模板的作用域。模板可能会有角度表达式,这将创建观察者。
当从DOM树中删除由模板和范围表示的元素内容时,如何删除使用它创建的观察者?
这个link证明了观察者即使从树中删除它所代表的DOM也会停留。
第一次单击编译按钮将编译角度模板并将其附加到DOM树。第二次单击该按钮时,它将清空添加了上一个模板的元素并添加新编译的模板。
的index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="jquery@1.7.2" data-semver="1.7.2" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script data-require="angular.js@1.2.6" data-semver="1.2.6" src="https://code.angularjs.org/1.2.6/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="smart-table-debug.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="controller">
<h1>Compile Demo</h1>
HTML to compile<br/>
<textarea id="html" ng-model="html"></textarea>
<br/>
Result<br/>
<div id="result"></div>
<br/>
<input type="button" value="compile" ng-click="compile()" />
<br/>
Compile time: {{ compileTime }}<br/>
Watchers count: {{ watchersCount }}<br/>
Digest count: {{ digestCount }} <br/>
Total time taken: {{ totalTime }} milliseconds <br/>
</body>
</html>
的script.js
function getAllScopes( rootScope, allScopes ) {
if( !allScopes ) {
allScopes = [];
}
allScopes.push( rootScope );
for( var scope = rootScope.$$childHead; scope; scope = scope.$$nextSibling ) {
getAllScopes( scope, allScopes );
}
return allScopes;
}
angular.module( "app", [] )
.controller( "controller", ["$scope", "$compile", "$rootScope", "$timeout", function($scope, $compile, $rootScope, $timeout ){
var e = angular.element;
$scope.html = "<div>{{watchersCount}}</div>"
$scope.watchersCount = 0;
$scope.digestCount = 0;
$scope.compileClickStart = performance.now();
$scope.totalTime = 0;
/* because we didn't gave watch expression as 1st parameter but a functionn,
* the function will be called at every end of digest cycle.
* https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest
*/
$rootScope.$watch(function() {
$scope.digestCount += 1;
$scope.totalTime = performance.now() - $scope.compileClickStart;
});
$scope.compile = function(){
$scope.digestCount = 0;
$scope.compileClickStart = performance.now();
var insertTarget = e('#result');
var targetScope = $scope.$new();
insertTarget.empty();
t0 = performance.now();
var expandedContent = $compile($scope.html)(targetScope);
t1 = performance.now();
$scope.compileTime = (t1 - t0) + " milliseconds.";
insertTarget.append( expandedContent );
$timeout( function() {
var allScopes = getAllScopes($rootScope);
var allWatchers = [];
for( var i = 0; i < allScopes.length; i++ ) {
var scope = allScopes[i];
if( scope.$$watchers) {
//allWatchers = allWatchers.concat( scope.$$watchers );
for( var j = 0; j < scope.$$watchers.length; j++ ) {
allWatchers.push({
"scope" : scope,
"watcher" : scope.$$watchers[j]
});
}
}
}
console.log( allScopes );
$scope.watchersCount = allWatchers.length;
});
};
}]);
答案 0 :(得分:5)
如TGH所述,观察者通常会在其所在的DOM和范围被删除时被移除。如果您没有这样做,那么您可以获得对元素范围的引用并手动销毁范围,删除观察者。
为简洁起见,省略了不相关的代码。
var expandedContent;
$scope.compile = function(){
// ...
var insertTarget = e('#result');
var targetScope = $scope.$new();
if (expandedContent) {
expandedContent.scope().$destroy();
}
insertTarget.empty();
expandedContent = $compile($scope.html)(targetScope);
insertTarget.append( expandedContent );
// ...
}
如果您已在指令中使用此功能,则在链接功能中完成后会更加清晰。侦听元素从DOM中删除并相应地清理范围。
link: function (scope, element, attrs) {
element.on("$destroy", function () {
scope.$destroy();
})
}
答案 1 :(得分:2)
如果销毁了DOM和相关范围,那么该内容的观察者也会消失。