我使用ng-repeat创建元素(有些是SVG标签,有些是简单的HTML)。在数据模型的变化 - 在新数据到达时重置的对象 - 总是留下作为分离的DOM元素的元素。它们是这样的:
元素是data_user的一部分,它似乎是jquery的一部分。在数据更改的几个地方会出现此问题。似乎观察者是问题,因为他们一直在参考他们的表达。
创建元素,例如像这样:
.directive('svgGraphic', ['$compile', function ($compile) {
return {
restrict: 'E',
replace: false,
link: function (scope, element, attrs) {
var svgData = scope.model.getAttribute("svgGraphic");
var svgDomElement = $(svgData.svg);
scope.layers = svgData.layers;
svgDomElement.append('<svg-layer ng-repeat="layer in layers"></svg-layer>');
element.append($compile(svgDomElement)(scope));
scope.$on("$destroy", function() {
scope.$$watchers = null;
scope.$$listeners = null;
})
}
};
}])
解决方法是手动删除观察者和听众,如上所示 - 我认为什么是不好的解决方案!
当来自服务器的新数据到达时,它设置如下:
$scope.model = model;
$scope.$digest();
仅替换模型数据是否有问题?
有什么想法角度如何不会删除旧元素上的听众?当ng-repeat接收新数据并重建所有元素时,Angular应删除所有观察者。
答案 0 :(得分:1)
我发现了同样的问题。我创建了一个Watcher类,然后使用分析器,我可以计算Watcher实例。当我浏览应用程序时,我看到实例继续增加,一些实例由data_user缓存保留:(。
我还修复了删除childScopes观察者的问题,并在范围内添加了一些元数据,如childScope列表。
这是我更改的角度代码(仅我更改的功能)。我希望这可以帮助你找到错误,我仍在与之抗争:)
function $RootScopeProvider() {
this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
function($injector, $exceptionHandler, $parse, $browser) {
var watcherCount = 0;
function Watcher(listener, initWatchVal, get, watchExp, objectEquality, scope) {
this.fn = isFunction(listener) ? listener : noop;
this.last = initWatchVal;
this.get = get;
this.exp = watchExp;
this.eq = !!objectEquality;
this.scope = scope;
this.id = watcherCount++;
}
Watcher.prototype = {
constructor: Watcher
}
function Scope() {
this.$id = nextUid();
this.$$phase = this.$parent = this.$$watchers =
this.$$nextSibling = this.$$prevSibling =
this.$$childHead = this.$$childTail = null;
this.$root = this;
this.$$destroyed = false;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$isolateBindings = null;
this.childsScopes = [];
}
Scope.prototype = {
constructor: Scope,
$new: function(isolate, parent) {
var child;
parent = parent || this;
if (isolate) {
child = new Scope();
child.$root = this.$root;
} else {
// Only create a child scope class if somebody asks for one,
// but cache it to allow the VM to optimize lookups.
if (!this.$$ChildScope) {
this.$$ChildScope = function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$id = nextUid();
this.$$ChildScope = null;
};
this.$$ChildScope.prototype = this;
}
child = new this.$$ChildScope();
}
//window.scopes = window.scopes || {};
//window.scopes[child.$id] = child;
this.childsScopes.push(child);
child.$parent = parent;
child.$$prevSibling = parent.$$childTail;
if (parent.$$childHead) {
parent.$$childTail.$$nextSibling = child;
parent.$$childTail = child;
} else {
parent.$$childHead = parent.$$childTail = child;
}
// When the new scope is not isolated or we inherit from `this`, and
// the parent scope is destroyed, the property `$$destroyed` is inherited
// prototypically. In all other cases, this property needs to be set
// when the parent scope is destroyed.
// The listener needs to be added after the parent is set
if (isolate || parent != this) child.$on('$destroy', destroyChild);
return child;
function destroyChild() {
child.$$destroyed = true;
child.$$watchers = null;
child.$$listeners = {};
//child.$parent = null;
child.$$nextSibling = null;
child.$$childHead = null;
child.$$childTail = null;
child.$$prevSibling = null;
child.$$listenerCount = {};
if (child.$parent) {
var index = child.$parent.childsScopes.indexOf(child);
child.$parent.childsScopes.splice(index, 1);
}
console.log("Destroying childScope " + child.$id);
}
}
$destroy: function() {
// we can't destroy the root scope or a scope that has been already destroyed
if (this.$$destroyed) return;
var parent = this.$parent;
console.log('Destroying Scope '+ this.$id);
//delete window.scopes[this.$id];
this.$broadcast('$destroy');
this.$$destroyed = true;
if (this === $rootScope) return;
for (var eventName in this.$$listenerCount) {
decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
}
// sever all the references to parent scopes (after this cleanup, the current scope should
// not be retained by any of our references and should be eligible for garbage collection)
if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
// Disable listeners, watchers and apply/digest methods
this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
this.$$listeners = {};
// All of the code below is bogus code that works around V8's memory leak via optimized code
// and inline caches.
//
// see:
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
this.$$childTail = this.$root = this.$$watchers = null;
}
}];
}
答案 1 :(得分:0)
删除缓存的SVG数据。
(function () {
"use strict";
angular.module('ApplicationModule').directive("ngD3SvgRefreshDirective", ["$rootScope", function ($rootScope) {
return {
restrict: 'AE',
templateUrl: 'pages/directive/D3SvgMyRefereshDirective.html',
scope: {
svgData: "=svgData",
reloadSvg: "=reloadSvg",
},
controller: function ($scope, $sce, $rootScope) {
$scope.$watch('reloadSvg', function (nu, old) {
$scope.init();
}, true);
$scope.init = function () {
if ($scope.svgData && $scope.svgData.layoutURI) {
console.log("data");
if ($scope.svgData) {
// if data is present ###
var svgDiv = angular.element(document.querySelector('#svgDiv')).html("");
d3.xml($scope.svgData.layoutURI, "image/svg+xml", function (xml) {
svgDiv.append(xml.documentElement);
$scope.styleSVGPathsNaddCallBack();
});
}
}
else {
// if data is not present ###
var svgDiv = null;
// selecting all the svg div which already present in browser ###
var svg = d3.select("svg");
svg.selectAll("*").remove();
console.log(svg);
}
};
$scope.styleSVGPathsNaddCallBack = function () {
var svgObject = d3.select('#svgDiv').select('svg');
svgObject.attr("width", "100.0%");
var objColor;
if (true) {
// your requirement logic
}
};
}
};
}]);
})();
控制器和服务相结合。数据注册控制器功能。
$scope.dataRegisteryForMyDirective = function (data, dataFlag) {
if (dataFlag) {
var svgDataMaster = data;
var svgData = {};
$scope.svgData = {};
svgData.layoutURI = data.layoutURI;
$scope.svgData = angular.copy(svgDataMaster);
$rootScope.reloadSvg = !$rootScope.reloadSvg;
}
else {
// making svg data empty, so we can execute the else block of directive --> init() and remove the cached data ###
$scope.svgData = {};
$rootScope.reloadSvg = !$rootScope.reloadSvg;
}
}
指令的服务调用和数据注册表调用
$scope.loadSvgByNumber = function (inputNumber) {
var d = inputNumber;
myService.getData(d).then(function (resp) {
if (resp.status == "FAILURE") {
$scope.svgTempData = [];
$scope.dataRegisteryForMyDirective(resp, false);
// calling to dataRegisteryForMyDirective with false flag
}
else {
$scope.dataRegisteryForMyDirective(resp, true);
// calling to dataRegisteryForMyDirective with true flag
}
});
};