Hackernoon logoReplacing the angular 1 router with Elm — Part 4 by@julianjelfs_61852

Replacing the angular 1 router with Elm — Part 4

image
Boolean Julian Jәlfs Hacker Noon profile picture

Boolean Julian Jәlfs

This is part four in a series. You should also read part 1, part 2 and part 3.

So we have at least one remaining major problem. When Elm removes the node at the root of our component tree as part of a route change we are potentially removing a large sub-tree that is under angular’s control without telling angular and without giving it a chance to perform its normal cleanup operations. This can and does lead to memory leaks and eventually crashes. You won’t notice this in the sample code because the effect is probably too small.

To recap, when Elm changes route we trigger a digest loop as follows:

function triggerDigest() {
if(!compile) {
return;
}
var $body = angular.element(document.body);
var $rootScope = $body.injector().get('$rootScope');
var $location = $body.injector().get('$location');
var $compile = $body.injector().get('$compile');
    $location.$$parseLinkUrl(window.location.href);
$rootScope.$apply(function() {
$compile($body)($rootScope);
});
observer.disconnect();
listenForRouteChanges = true;
}

In order to preserve the $destroy behaviour of angular and hopefully avoid memory leaks we need to do something different. We must create a new scope each time from the $rootScope and then compile our root node against that new scope. Then in the next iteration, if that scope we created exists, we will call $destroy on it before we overwrite it with the next new scope. This way, angular will cascade the $destroy call down through the scope hierarchy and everything that needs to be cleaned up will be cleaned up. So the code above will now look like this:

let activeScope = null;
function triggerDigest() {
if(activeScope) {
activeScope.$destroy();
}
    if(!compile) {
return;
}
var $body = angular.element(document.body);
var $rootScope = $body.injector().get('$rootScope');
var $location = $body.injector().get('$location');
var $compile = $body.injector().get('$compile');
    activeScope = $rootScope.$new();
    $location.$$parseLinkUrl(window.location.href);
activeScope.$apply(function() {
$compile($body)(activeScope);
});

observer.disconnect();
listenForRouteChanges = true;
}

Using the AngularJS chrome plugin it is now easy to see that scope hierarchy is now being pruned on each route change as we would expect and hopefully, the memory leak problem goes away (or at least is no worse than it might be with regular angular).

As before, full source for this proof of concept can be found here.

Tags

Join Hacker Noon

Create your free account to unlock your custom reading experience.