fix(Router): replace state when normalized path is equal to current normalized path
Make sure the same path is not added multiple times to the history. It is replacing the state, instead of skipping it completely, because the current path in the browser might not be normalized, while the given one is normalized. Closes #7829 Closes #7897
This commit is contained in:
parent
9105ab9596
commit
2bf21e1747
|
@ -63,6 +63,13 @@ export class Location {
|
||||||
*/
|
*/
|
||||||
path(): string { return this.normalize(this.platformStrategy.path()); }
|
path(): string { return this.normalize(this.platformStrategy.path()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the given path and compares to the current normalized path.
|
||||||
|
*/
|
||||||
|
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||||
|
return this.path() == this.normalize(path + Location.normalizeQueryParams(query));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a string representing a URL, returns the normalized URL path without leading or
|
* Given a string representing a URL, returns the normalized URL path without leading or
|
||||||
* trailing slashes
|
* trailing slashes
|
||||||
|
|
|
@ -23,6 +23,14 @@ export class SpyLocation implements Location {
|
||||||
|
|
||||||
path(): string { return this._history[this._historyIndex].path; }
|
path(): string { return this._history[this._historyIndex].path; }
|
||||||
|
|
||||||
|
isCurrentPathEqualTo(path: string, query: string = ''): boolean {
|
||||||
|
var givenPath = path.endsWith('/') ? path.substring(0, path.length - 1) : path;
|
||||||
|
var currPath =
|
||||||
|
this.path().endsWith('/') ? this.path().substring(0, this.path().length - 1) : this.path();
|
||||||
|
|
||||||
|
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
||||||
|
}
|
||||||
|
|
||||||
simulateUrlPop(pathname: string) {
|
simulateUrlPop(pathname: string) {
|
||||||
ObservableWrapper.callEmit(this._subject, {'url': pathname, 'pop': true});
|
ObservableWrapper.callEmit(this._subject, {'url': pathname, 'pop': true});
|
||||||
}
|
}
|
||||||
|
@ -62,8 +70,13 @@ export class SpyLocation implements Location {
|
||||||
replaceState(path: string, query: string = '') {
|
replaceState(path: string, query: string = '') {
|
||||||
path = this.prepareExternalUrl(path);
|
path = this.prepareExternalUrl(path);
|
||||||
|
|
||||||
this._history[this._historyIndex].path = path;
|
var history = this._history[this._historyIndex];
|
||||||
this._history[this._historyIndex].query = query;
|
if (history.path == path && history.query == query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
history.path = path;
|
||||||
|
history.query = query;
|
||||||
|
|
||||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||||
this.urlChanges.push('replace: ' + url);
|
this.urlChanges.push('replace: ' + url);
|
||||||
|
|
|
@ -526,8 +526,12 @@ export class RootRouter extends Router {
|
||||||
}
|
}
|
||||||
var promise = super.commit(instruction);
|
var promise = super.commit(instruction);
|
||||||
if (!_skipLocationChange) {
|
if (!_skipLocationChange) {
|
||||||
|
if (this._location.isCurrentPathEqualTo(emitPath, emitQuery)) {
|
||||||
|
promise = promise.then((_) => { this._location.replaceState(emitPath, emitQuery); });
|
||||||
|
} else {
|
||||||
promise = promise.then((_) => { this._location.go(emitPath, emitQuery); });
|
promise = promise.then((_) => { this._location.go(emitPath, emitQuery); });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,22 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should replace state when normalized paths are equal',
|
||||||
|
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||||
|
compile(tcb)
|
||||||
|
.then((rtc) => {fixture = rtc})
|
||||||
|
.then((_) => location.setInitialPath("/test/"))
|
||||||
|
.then((_) => rtr.config([new Route({path: '/test', component: HelloCmp})]))
|
||||||
|
.then((_) => rtr.navigateByUrl('/test'))
|
||||||
|
.then((_) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||||
|
expect(location.urlChanges).toEqual(['replace: /test']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should reuse common parent components', inject([AsyncTestCompleter], (async) => {
|
it('should reuse common parent components', inject([AsyncTestCompleter], (async) => {
|
||||||
compile(tcb)
|
compile(tcb)
|
||||||
.then((rtc) => {fixture = rtc})
|
.then((rtc) => {fixture = rtc})
|
||||||
|
|
|
@ -313,9 +313,18 @@ Location.prototype.subscribe = function () {
|
||||||
Location.prototype.path = function () {
|
Location.prototype.path = function () {
|
||||||
return $location.url();
|
return $location.url();
|
||||||
};
|
};
|
||||||
|
Location.prototype.isCurrentPathEqualTo = function (path, query) {
|
||||||
|
if (query === void 0) { query = ''; }
|
||||||
|
return this.path() === (path + query);
|
||||||
|
};
|
||||||
Location.prototype.go = function (path, query) {
|
Location.prototype.go = function (path, query) {
|
||||||
return $location.url(path + query);
|
return $location.url(path + query);
|
||||||
};
|
};
|
||||||
|
Location.prototype.replaceState = function (path, query) {
|
||||||
|
if (query === void 0) { query = ''; }
|
||||||
|
$location.url(path + query);
|
||||||
|
$location.replace();
|
||||||
|
};
|
||||||
Location.prototype.prepareExternalUrl = function(url) {
|
Location.prototype.prepareExternalUrl = function(url) {
|
||||||
if (url.length > 0 && !url.startsWith('/')) {
|
if (url.length > 0 && !url.startsWith('/')) {
|
||||||
url = '/' + url;
|
url = '/' + url;
|
||||||
|
|
Loading…
Reference in New Issue