diff --git a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js index f7fbd2e8f6..778d238e47 100644 --- a/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js +++ b/awx/ui/client/src/job-results/host-status-bar/host-status-bar.directive.js @@ -15,7 +15,7 @@ export default [ 'templateUrl', link: function(scope) { // as count is changed by event data coming in, // update the host status bar - scope.$watch('count', function(val) { + var toDestroy = scope.$watch('count', function(val) { if (val) { Object.keys(val).forEach(key => { // reposition the hosts status bar by setting @@ -38,6 +38,10 @@ export default [ 'templateUrl', .filter(key => (val[key] > 0)).length > 0); } }); + + scope.$on('$destroy', function(){ + toDestroy(); + }); } }; }]; diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js index 77c87c74da..d6f0ee8e04 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.directive.js @@ -12,6 +12,18 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', templateUrl: templateUrl('job-results/job-results-stdout/job-results-stdout'), restrict: 'E', link: function(scope, element) { + var toDestroy = [], + resizer, + scrollWatcher; + + scope.$on('$destroy', function(){ + $(window).off("resize", resizer); + $(window).off("scroll", scrollWatcher); + $(".JobResultsStdOut-stdoutContainer").off('scroll', + scrollWatcher); + toDestroy.forEach(v => v()); + }); + scope.stdoutContainerAvailable.resolve("container available"); // utility function used to find the top visible line and // parent header in the pane @@ -115,9 +127,15 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', // stop iterating over the standard out // lines once the first one has been // found + + $this = null; return false; - } - }); + } + + $this = null; + }); + + $container = null; return { visLine: visItem, @@ -131,22 +149,24 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', } else { scope.isMobile = false; } - // watch changes to the window size - $(window).resize(function() { + + resizer = function() { // and update the isMobile var accordingly if (window.innerWidth <= 1200 && !scope.isMobile) { scope.isMobile = true; } else if (window.innerWidth > 1200 & scope.isMobile) { scope.isMobile = false; } - }); + }; + // watch changes to the window size + $(window).resize(resizer); var lastScrollTop; var initScrollTop = function() { lastScrollTop = 0; }; - var scrollWatcher = function() { + scrollWatcher = function() { var st = $(this).scrollTop(); var netScroll = st + $(this).innerHeight(); var fullHeight; @@ -178,11 +198,15 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', } lastScrollTop = st; + + st = null; + netScroll = null; + fullHeight = null; }; // update scroll watchers when isMobile changes based on // window resize - scope.$watch('isMobile', function(val) { + toDestroy.push(scope.$watch('isMobile', function(val) { if (val === true) { // make sure ^ TOP always shown for mobile scope.stdoutOverflowed = true; @@ -204,7 +228,7 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', $(".JobResultsStdOut-stdoutContainer").on('scroll', scrollWatcher); } - }); + })); // called to scroll to follow anchor scope.followScroll = function() { @@ -237,7 +261,7 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', // if following becomes active, go ahead and get to the bottom // of the standard out pane - scope.$watch('followEngaged', function(val) { + toDestroy.push(scope.$watch('followEngaged', function(val) { // scroll to follow point if followEngaged is true if (val) { scope.followScroll(); @@ -251,7 +275,7 @@ export default [ 'templateUrl', '$timeout', '$location', '$anchorScroll', scope.followTooltip = "Click to follow standard out as it comes in."; } } - }); + })); // follow button ng-click function scope.followToggleClicked = function() { diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js index 56aed6ce5b..c2c1f99df3 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -1,5 +1,7 @@ export default ['jobData', 'jobDataOptions', 'jobLabels', 'jobFinished', 'count', '$scope', 'ParseTypeChange', 'ParseVariableString', 'jobResultsService', 'eventQueue', '$compile', '$log', 'Dataset', '$q', 'Rest', '$state', 'QuerySet', '$rootScope', 'moment', function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTypeChange, ParseVariableString, jobResultsService, eventQueue, $compile, $log, Dataset, $q, Rest, $state, QuerySet, $rootScope, moment) { + var toDestroy = []; + var cancelRequests = false; // used for tag search $scope.job_event_dataset = Dataset.data; @@ -66,14 +68,14 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy // update label in left pane and tooltip in right pane when the job_status // changes - $scope.$watch('job_status', function(status) { + toDestroy.push($scope.$watch('job_status', function(status) { if (status) { $scope.status_label = $scope.jobOptions.status.choices .filter(val => val[0] === status) .map(val => val[1])[0]; $scope.status_tooltip = "Job " + $scope.status_label; } - }); + })); // update the job_status value. Use the cached rootScope value if there // is one. This is a workaround when the rest call for the jobData is @@ -278,6 +280,9 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy .stdout)($scope.events[mungedEvent .counter])); } + + classList = null; + putIn = null; } else { // this is a header or recap line, so just // append to the bottom @@ -368,7 +373,7 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy delete event.event; processEvent(event); }); - if (events.next) { + if (events.next && !cancelRequests) { getEvents(events.next); } else { // put those paused events into the pane @@ -378,7 +383,7 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy }; // grab non-header recap lines - $scope.$watch('job_event_dataset', function(val) { + toDestroy.push($scope.$watch('job_event_dataset', function(val) { // pause websocket events from coming in to the pane $scope.gotPreviouslyRanEvents = $q.defer(); @@ -391,19 +396,19 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy delete event.event; processEvent(event); }); - if (val.next) { + if (val.next && !cancelRequests) { getEvents(val.next); } else { // put those paused events into the pane $scope.gotPreviouslyRanEvents.resolve(""); } }); - }); + })); // Processing of job_events messages from the websocket - $scope.$on(`ws-job_events-${$scope.job.id}`, function(e, data) { + toDestroy.push($scope.$on(`ws-job_events-${$scope.job.id}`, function(e, data) { $q.all([$scope.gotPreviouslyRanEvents.promise, $scope.hasSkeleton.promise]).then(() => { var url = Dataset @@ -446,10 +451,10 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy } }); - }); + })); // Processing of job-status messages from the websocket - $scope.$on(`ws-jobs`, function(e, data) { + toDestroy.push($scope.$on(`ws-jobs`, function(e, data) { if (parseInt(data.unified_job_id, 10) === parseInt($scope.job.id,10)) { // controller is defined, so set the job_status @@ -477,5 +482,18 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy // for this job. cache the socket status on root scope $rootScope['lastSocketStatus' + data.unified_job_id] = data.status; } + })); + + $scope.$on('$destroy', function(){ + cancelRequests = true; + eventQueue.initialize(); + Object.keys($scope.events) + .forEach(v => { + $scope.events[v].$destroy(); + $scope.events[v] = null; + }); + $scope.events = {}; + clearInterval(elapsedInterval); + toDestroy.forEach(v => v()); }); }]; diff --git a/awx/ui/client/src/job-results/parse-stdout.service.js b/awx/ui/client/src/job-results/parse-stdout.service.js index 8d6564f3f0..858d86efe2 100644 --- a/awx/ui/client/src/job-results/parse-stdout.service.js +++ b/awx/ui/client/src/job-results/parse-stdout.service.js @@ -185,7 +185,6 @@ export default ['$log', 'moment', function($log, moment){ data-uuid="${clickClass}"> `; - // console.log(expandDom); return expandDom; } else { // non-header lines don't get an expander