fix(common): prevent duplicate URL change notifications (#37404)

Prevent duplicate notifications from being emitted when multiple URL change listeners are registered using Location#onUrlChange.

PR Close #37404
This commit is contained in:
Lars Gyrup Brink Nielsen 2020-06-02 23:05:39 +02:00 committed by atscott
parent eb6ba9ac80
commit 3569fdf451
2 changed files with 34 additions and 5 deletions

View File

@ -64,6 +64,7 @@ export class Location {
_platformLocation: PlatformLocation;
/** @internal */
_urlChangeListeners: ((url: string, state: unknown) => void)[] = [];
private _urlChangeSubscription?: SubscriptionLike;
constructor(platformStrategy: LocationStrategy, platformLocation: PlatformLocation) {
this._platformStrategy = platformStrategy;
@ -194,10 +195,13 @@ export class Location {
*/
onUrlChange(fn: (url: string, state: unknown) => void) {
this._urlChangeListeners.push(fn);
this.subscribe(v => {
if (!this._urlChangeSubscription) {
this._urlChangeSubscription = this.subscribe(v => {
this._notifyUrlChangeListeners(v.url, v.state);
});
}
}
/** @internal */
_notifyUrlChangeListeners(url: string = '', state: unknown) {

View File

@ -7,7 +7,7 @@
*/
import {CommonModule, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common';
import {MockPlatformLocation} from '@angular/common/testing';
import {MockLocationStrategy, MockPlatformLocation} from '@angular/common/testing';
import {inject, TestBed} from '@angular/core/testing';
const baseUrl = '/base';
@ -84,7 +84,7 @@ describe('Location Class', () => {
TestBed.configureTestingModule({
imports: [CommonModule],
providers: [
{provide: LocationStrategy, useClass: PathLocationStrategy},
{provide: LocationStrategy, useClass: MockLocationStrategy},
{
provide: PlatformLocation,
useFactory: () => {
@ -113,5 +113,30 @@ describe('Location Class', () => {
expect((location as any)._urlChangeListeners.length).toBe(1);
expect((location as any)._urlChangeListeners[0]).toEqual(changeListener);
}));
it('should only notify listeners once when multiple listeners are registered', () => {
const location = TestBed.inject(Location);
const locationStrategy = TestBed.inject(LocationStrategy) as MockLocationStrategy;
let notificationCount = 0;
function incrementChangeListener(url: string, state: unknown) {
notificationCount += 1;
return undefined;
}
function noopChangeListener(url: string, state: unknown) {
return undefined;
}
location.onUrlChange(incrementChangeListener);
location.onUrlChange(noopChangeListener);
expect(notificationCount).toBe(0);
locationStrategy.simulatePopState('/test');
expect(notificationCount).toBe(1);
});
});
});