fix(router): avoid freezing queryParams in-place (#22663)

The recognizer code used to call Object.freeze() on queryParams before
using them to construct ActivatedRoutes, with the intent being to help
avoid common invalid usage. Unfortunately, Object.freeze() works
in-place, so this was also freezing the queryParams on the actual
UrlTree object, making it more difficult to manipulate UrlTrees in
things like UrlHandlingStrategy.

This change simply shallow-copies the queryParams before freezing them.

Fixes #22617

PR Close #22663
This commit is contained in:
Zaven Muradyan 2018-03-08 12:16:01 -08:00 committed by Igor Minar
parent 553a680817
commit 89f64e58c3
2 changed files with 13 additions and 4 deletions

View File

@ -38,8 +38,9 @@ class Recognizer {
const children = this.processSegmentGroup(this.config, rootSegmentGroup, PRIMARY_OUTLET); const children = this.processSegmentGroup(this.config, rootSegmentGroup, PRIMARY_OUTLET);
const root = new ActivatedRouteSnapshot( const root = new ActivatedRouteSnapshot(
[], Object.freeze({}), Object.freeze(this.urlTree.queryParams), this.urlTree.fragment !, [], Object.freeze({}), Object.freeze({...this.urlTree.queryParams}),
{}, PRIMARY_OUTLET, this.rootComponentType, null, this.urlTree.root, -1, {}); this.urlTree.fragment !, {}, PRIMARY_OUTLET, this.rootComponentType, null,
this.urlTree.root, -1, {});
const rootNode = new TreeNode<ActivatedRouteSnapshot>(root, children); const rootNode = new TreeNode<ActivatedRouteSnapshot>(root, children);
const routeState = new RouterStateSnapshot(this.url, rootNode); const routeState = new RouterStateSnapshot(this.url, rootNode);
@ -116,7 +117,7 @@ class Recognizer {
if (route.path === '**') { if (route.path === '**') {
const params = segments.length > 0 ? last(segments) !.parameters : {}; const params = segments.length > 0 ? last(segments) !.parameters : {};
snapshot = new ActivatedRouteSnapshot( snapshot = new ActivatedRouteSnapshot(
segments, params, Object.freeze(this.urlTree.queryParams), this.urlTree.fragment !, segments, params, Object.freeze({...this.urlTree.queryParams}), this.urlTree.fragment !,
getData(route), outlet, route.component !, route, getSourceSegmentGroup(rawSegment), getData(route), outlet, route.component !, route, getSourceSegmentGroup(rawSegment),
getPathIndexShift(rawSegment) + segments.length, getResolve(route)); getPathIndexShift(rawSegment) + segments.length, getResolve(route));
} else { } else {
@ -125,7 +126,7 @@ class Recognizer {
rawSlicedSegments = segments.slice(result.lastChild); rawSlicedSegments = segments.slice(result.lastChild);
snapshot = new ActivatedRouteSnapshot( snapshot = new ActivatedRouteSnapshot(
consumedSegments, result.parameters, Object.freeze(this.urlTree.queryParams), consumedSegments, result.parameters, Object.freeze({...this.urlTree.queryParams}),
this.urlTree.fragment !, getData(route), outlet, route.component !, route, this.urlTree.fragment !, getData(route), outlet, route.component !, route,
getSourceSegmentGroup(rawSegment), getSourceSegmentGroup(rawSegment),
getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route)); getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route));

View File

@ -748,6 +748,14 @@ describe('recognize', () => {
expect(Object.isFrozen(s.root.queryParams)).toBeTruthy(); expect(Object.isFrozen(s.root.queryParams)).toBeTruthy();
}); });
}); });
it('should not freeze UrlTree query params', () => {
const url = tree('a?q=11');
recognize(RootComponent, [{path: 'a', component: ComponentA}], url, 'a?q=11')
.subscribe((s: RouterStateSnapshot) => {
expect(Object.isFrozen(url.queryParams)).toBe(false);
});
});
}); });
describe('fragment', () => { describe('fragment', () => {