'Destroying $scope manually
tl;dr
Application loads modules, refreshes every X minutes, does not destroy module instances on reloading. $scope.$on('$destroy') does not get called. Modules DDoS API and slow down application. Manual $broadcast from applicaton controller to trigger a destroy (code below). How to manually unload a controller instance when $scope.$destroy() gives "current is null" error?
More detailed explanation
I'm currently working on a dashboard style application which uses angularjs modules as widgets. These are being dynamically loaded using ocLazyLoad to reduce application load times. All content comes from a JSON API.
The dashboard reloads it's widget every 30 minutes (update code below), also rebuilding the view. When this happens, the existing widgets do not get destroyed and remain in the memory. Meaning every X minutes they are set to update also continues to happen. Thus when letting the application run for 2 hours, there's 4 instances of the widgets asking for data every X seconds. When the dashboard reloads its widgets, there's no destroy event getting called on the old instances as these are never destroyed.
I've partially fixed this by having the dashboard controller broadcast a destroy event so the running controllers clean up their $timeouts, however this does obviously not remove the loaded instances of the widgets from the memory. Trying to call $scope.$destroy() results in a current is null error, and using the element remove code below.
The question is, how else can I destroy these controllers?
code blocks
Destroy broadcast receiver
$scope.$on("destroy", function () {
console.log("RMA Widget scope received destroy event. Destroying RMA widget");
$scope.$destroy();
});
Remove module (widget) elements
document.querySelectorAll(".widget").forEach((el) => {
el.remove()
});
Loading modules (widgets). $scope.widgets is used in ng-repeat in the body.
$scope.widgets = [];
for (let i = 0; i < rqData.data.data.widgets.length; i++) {
const widget = rqData.data.data.widgets[i];
$ocLazyLoad.load('app/' + widget.type + '/module.js');
$scope.widgets.push({
directive : widget.type + '-widget',
module : 'app/' + widget.type + '/module.js',
view : 'app/' + widget.type + '/view.html',
data : Object.assign(widget.widget_id,
{item_id : widget.id, content : widget.content}),
});
}
$scope.timeout.compile = $timeout($ctrl.compileView, 3000);
Solution 1:[1]
If you are using ng-repeat it should be enough to delete the variable $scope.widgets and set it again. And i mean really delete the var with delete $scope.widgets
Maybe like this:
function set_widgets(){
if($scope.widgets !== undefined){
delete $scope.widgets;
}
$scope.widgets = [];
for (let i = 0; i < rqData.data.data.widgets.length; i++) {
const widget = rqData.data.data.widgets[i];
$ocLazyLoad.load('app/' + widget.type + '/module.js');
$scope.widgets.push({
directive : widget.type + '-widget',
module : 'app/' + widget.type + '/module.js',
view : 'app/' + widget.type + '/view.html',
data : Object.assign(widget.widget_id,
{item_id : widget.id, content : widget.content}),
});
}
}
Edit: All $intervals and $timeouts inside the widgets controller need to be stopped by yourself that isn't something that is bind to the scope of the controller
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
