Permalink
Browse files

perf($rootScope): remove history event handler when app is torn down

Remember the popstate and hashchange handler registered with window
when the application bootstraps, and remove it when the application
is torn down

Closes #9897
Closes #9905
  • Loading branch information...
1 parent 79fa7dd commit d996305b4470f80fbb1cbddf54b7d10ffbb6ab47 @randombk randombk committed with petebacondarwin Nov 4, 2014
Showing with 40 additions and 0 deletions.
  1. +10 −0 src/ng/browser.js
  2. +5 −0 src/ng/rootScope.js
  3. +1 −0 src/ngMock/angular-mocks.js
  4. +17 −0 test/ng/browserSpecs.js
  5. +7 −0 test/ng/rootScopeSpec.js
View
@@ -265,6 +265,16 @@ function Browser(window, document, $log, $sniffer) {
};
/**
+ * @private
+ * Remove popstate and hashchange handler from window.
+ *
+ * NOTE: this api is intended for use only by $rootScope.
+ */
+ self.$$applicationDestroyed = function() {
+ jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
+ };
+
+ /**
* Checks whether the url has changed outside of Angular.
* Needs to be exported to be able to check for changes that have been done in sync,
* as hashchange/popstate events fire in async.
View
@@ -878,6 +878,11 @@ function $RootScopeProvider() {
this.$broadcast('$destroy');
this.$$destroyed = true;
+ if (this === $rootScope) {
+ //Remove handlers attached to window when $rootScope is removed
+ $browser.$$applicationDestroyed();
+ }
+
incrementWatchersCount(this, -this.$$watchersCount);
for (var eventName in this.$$listenerCount) {
decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
@@ -57,6 +57,7 @@ angular.mock.$Browser = function() {
return listener;
};
+ self.$$applicationDestroyed = angular.noop;
self.$$checkUrlChange = angular.noop;
self.deferredFns = [];
@@ -647,6 +647,23 @@ describe('browser', function() {
};
}
});
+
+
+ it("should stop calling callbacks when application has been torn down", function() {
+ sniffer.history = true;
+ browser.onUrlChange(callback);
+ fakeWindow.location.href = 'http://server/new';
+
+ browser.$$applicationDestroyed();
+
+ fakeWindow.fire('popstate');
+ expect(callback).not.toHaveBeenCalled();
+
+ fakeWindow.fire('hashchange');
+ fakeWindow.setTimeout.flush();
+ expect(callback).not.toHaveBeenCalled();
+ });
+
});
@@ -1048,6 +1048,13 @@ describe('Scope', function() {
}));
+ it('should call $browser.$$applicationDestroyed when destroying rootScope', inject(function($rootScope, $browser) {
+ spyOn($browser, '$$applicationDestroyed');
+ $rootScope.$destroy();
+ expect($browser.$$applicationDestroyed).toHaveBeenCalledOnce();
+ }));
+
+
it('should remove first', inject(function($rootScope) {
first.$destroy();
$rootScope.$digest();

0 comments on commit d996305

Please sign in to comment.