0

We are working on a SPA where there is a main page with its own controller and, within it, a multi-tabs-panes arrangement built using:

<div class="tab-content clearfix">
    <div class="tab-pane" 
         ng-repeat="One_Tab in Running_Tabs" 
         id="{{One_Tab.Page_Type}}_{{One_Tab.Instance}}" 
         ng-include="Build_Page_Reference(One_Tab.Page_Type)">
    </div>
</div>        

The problem, as explained below, is that once a tab is removed from screen, its controller's instance remains active.

Whenever the user clicks on a commands menu entry, a new element is added to $scope.Running_Tabs and the new tab is then displayed. Each tab, of course, is handled by its own controller. The user may open different pages as well as different instances of the same page, each with its own context (imagine, for instance, looking at the profies of different application users).

When the user wishes to close a specific tab, he clicks on an "X" at the tab managed by the main page's controller. A "close" event is then emmited and received by all open tabs' controllers. The controller of each tab checks if the event is meant to it (using the Tab_ID). If not, ignore the event. If yes, a controller-local cleanup take place and, when completed, a common function (see below) for the actual tab removal is invoked and the tab is removed from screen.

Following is the code of the common function that handles the actual tab removal (based on the following post: AngularJS not cleaning child scope created by ng-include):

function LLF_Remove_Tab (p_Tabs_Collection_Root , p_Tab_ID) {

    var l_Object ;

    for (var i = 0 ; i < p_Tabs_Collection_Root.length ; i++) {

        if (p_Tabs_Collection_Root[i].Tab_ID == p_Tab_ID) {

            l_Object = document.getElementById(p_Tabs_Collection_Root[i].Page_Type + "_" + 
                                               p_Tabs_Collection_Root[i].Instance) ;

            p_Tabs_Collection_Root.splice(i,1) ;

            angular.element(l_Object).scope().$destroy();

            l_Object.remove();

            break ;
        }
    }
}

The problem is that the context of the removed controllers still remain active. I added a simple console.log which is invoked when the tab receives the emmited close request (before checking if it is relevant or not). All the tabs that were previously removed using the above function still log into the console the reception of the event (and hence there is a good chance that there would be more than one tab with the same Tab_ID attempting to close).

So the question is: what is missing in the above function to make sure that the whole context of closed tabs is fully removed. Or, should I implement this in a different way?

FDavidov
  • 3,505
  • 6
  • 23
  • 59

1 Answers1

0

I am not sure why you are trying to perform manual removing, while simple approach should work:

Main Html:

<div class="tab-pane" ng-repeat="One_Tab in Running_Tabs" ng-include="One_Tab.template">
</div>

Main Controller:

var vm = this;
vm.removeTab = (tab) => {
    var index = vm.Running_Tabs.indexOf(tab);
    vm.Running_Tabs.splice(index, 1);
}

Tab template example:

<button ng-click="removeTab(One_Tab)">Close</button>

P.S. Also notice that using methods in tempales is bad idea usually, so better create Tab object (e.g. {}), calculate template and assign it as const property (e.g. {template: 'template1.html'}).

Petr Averyanov
  • 9,327
  • 3
  • 20
  • 38
  • Thank you Petr for your response. Essentially, we are doing exactly what you suggested and the tabs ARE removed from screen. The problem is that their respective controllers remain active (at the background if you wish) as evidenced by the fact that they keep writing to the console after their tabs were removed from screen. – FDavidov Aug 28 '19 at 09:14
  • this is not true... https://plnkr.co/edit/4B19j1j1UsRW2n5PDYQR?p=preview All work as expected. Now I think that you using $on from $rootScope -- is this true? – Petr Averyanov Aug 28 '19 at 09:40
  • `$on` from `$rootScope`: Yes, it is true. – FDavidov Aug 28 '19 at 09:57
  • than your problem is not one you described. Component is destroyed, but it is not collected by grabage collector as '$on' function keep referenced to it -- in general you should use $scope.$on as when $scope is destroyed, all listeners are deattached. If you call $rootScope.$on -- you must derigester manually in $onDestroy callback. – Petr Averyanov Aug 28 '19 at 10:42
  • Thank you Petr. We'll check and let you know. – FDavidov Aug 28 '19 at 13:16
  • Petr, though the solution I implemented and solved the issue is somewhat different from what you suggested, your comments turned on the light on the issue (hanging event listeners). As such, I marked your answer as correct. Thanks for your help. – FDavidov Aug 29 '19 at 09:17