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:
parent
553a680817
commit
89f64e58c3
|
@ -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));
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
Loading…
Reference in New Issue