-
Notifications
You must be signed in to change notification settings - Fork 490
Wen I create datatables and rows with angularjs $compile then increases memory leak in every dtInstante.reload() #1119
Comments
More info about. Even when use pagination is growing this becouse in paginations datatable execute createdRow() Is it possible destroy previous $compiles Rows before enter in createdRows and avoid this Big memory leak? |
Check #1118. |
I had a similar problem - I had event handlers in my angularjs components dynamically compiled in datatables rows; when datatable was reloaded or resorted, then event handlers were not removed. I have tried with: $element.on('$destroy', function () { /* deinit here */ }); but $destroy event was not triggered. // Directive is used to fix memory leaks related problems in scenario when angularjs $compile is used to dynamically render datatables
// row content(see $compile(angular.element(row).contents())($scope) in 'createdRow' callback: https://datatables.net/reference/option/createdRow).
// When datatables is reloaded (or rows are resorted) then angularjs directives compiled in old datatables rows are NOT destroyed.
// This is probably due to way that datatables are removing old nodes. I have discovered that it is performed using body.children().detach() (see _fnDraw in jquery.dataTables.js)...
// As you can see detach() jQuery method calls jQuery remove() method with keepData parameter set to true. When keepData is true then jQuery does NOT call jQuery.cleanData() method (see https://github.com/jquery/jquery/blob/2.2.4/src/manipulation.js#L212).
// When jQuery.cleanData() is NOT called then angularjs will NOT trigger '$destroy' event which could be used to eg. deinitialize directive components compuled in old datatables rows...
//
// This directive should wrap datatables for which $compile is used. There also should be '$destroy' event handler in all angularjs components used in dynamically compiled datatables rows, eg.:
// app.directive('xxx', function() {
// return {
// // ...
// link: function ($scope, $element, $attrs, $ctrl) {
// // ...
// $element.on('$destroy', function () {
// $scope.$destroy();
// });
// $scope.$on('$destroy', function () {
// // TODO: Add deinit code (eg. detach event handlers, etc.)
// });
// // ...
// },
// // ...
// };
// });
// TODO-readings: https://www.dwmkerr.com/fixing-memory-leaks-in-angularjs-applications/
app.directive('axDtTriggerDestroyOnRowRemoved', ['$log', function ($log) { // ax-dt-trigger-destroy-on-row-removed
return {
restrict: 'E',
link: function ($scope, $element, $attrs, $ctrl) {
// Idea of using MutationObserver taken from: https://stackoverflow.com/questions/44935865/detect-when-a-node-is-deleted-or-removed-from-the-dom-because-a-parent-was/44937162#44937162
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
// DEV HINT: https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord
// mutation.type: Returns "attributes" if the mutation was an attribute mutation, "characterData" if it was a mutation to a CharacterData node, and "childList" if it was a mutation to the tree of nodes.
// mutation.target: For childList, it is the node whose children changed.
if (mutation.removedNodes.length > 0 && mutation.type === 'childList' && $.fn.dataTable.isDataTable($(mutation.target).parents('table'))) {
var removedNodes = Array.from(mutation.removedNodes);
removedNodes.forEach(function (node) {
// eg. node => <tr class="ng-scope odd" role="row"><td>....</td></tr>
if (node.tagName === 'TR') {
var elementsWithDestroySupport = $(node)
.find('*') // find all children
.filter(function (idx, itm) { // find child directive nodes for which $destroy event could be triggered
var events = $._data(itm, 'events');
return events && events.$destroy;
});
// DEV HINT: See angular.js code for jQuery.cleanData = function(elems) // => https://github.com/angular/angular.js/blob/v1.6.10/src/Angular.js#L1944
if (elementsWithDestroySupport.length)
angular.forEach(elementsWithDestroySupport, function (el, idx) {
angular.element(el).triggerHandler('$destroy');
});
}
});
}
});
});
observer.observe($element[0], { subtree: true, childList: true });
// Directive deinit code
$element.on('$destroy', function () {
$log.debug('[axDtTriggerDestroyOnRowRemoved] Element destroy => ', $scope, $element);
observer.disconnect();
observer = undefined;
});
}
};
}]); I am wondering if it helps in your scenario (it for sure helped me to solve problems with detaching event handlers in components $compile-d in createdRow datatables callback). |
Uh oh!
There was an error while loading. Please reload this page.
First thing first:
Version of my angular-datatables lib is: v0.6.3-dev
Version of my jquery datatable lib is: DataTables 1.10.9
My code creating a angular-datatable :
So far alll is working and in this code the only important thing is I am using angular $compile in order to use ng-click in buttons in every row
The problem is when I reload the datable instance:
In every reload of this datatable instance, I do it like this:
$scope.dtOptionsCases.reloadData(null,false);
The big problems is in memory there are a lot of increasing amount of wathers (from every detached row, becouse these rows have ng-click compiled) A lot of big memory leaks are created evere reload.
How can I clear old rows before reload new rows. Is there a way to do this.
We are in a big problem with a client because of this and We´d apreciate any help.
Sorry my bad english and a lot of Thanks in advance for your help.
The text was updated successfully, but these errors were encountered: