Skip to content

Commit

Permalink
fix(route): make sure param maps contain the same values as params
Browse files Browse the repository at this point in the history
The ActivatedRouteSnapshot class of Angular only creates the param maps once, lazily, when it's requested for the first time. But the params are public, so changing them changes the params without changing the param maps. This commit circumvents this Angular bug by overriding the map getters in order to always return a new map created from the params.
  • Loading branch information
jnizet committed Jun 17, 2022
1 parent add37ac commit c7859a1
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
28 changes: 28 additions & 0 deletions projects/ngx-speculoos/src/lib/route.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,19 @@ describe('routes', () => {
expect(queryParamEmissionCount).toBe(1);
});

it('should set a param in the map too, even if the map has already been read before', () => {
const route = stubRoute();

expect(route.snapshot.paramMap.get('a')).toBeNull();

const expectedParams = { a: 'a2' };

route.setParam('a', 'a2');

expect(route.snapshot.params).toEqual(expectedParams);
expect(route.snapshot.paramMap.get('a')).toBe('a2');
});

it('should set a query param', () => {
const route = stubRoute({ queryParams: { a: 'a1', b: 'b1' } });

Expand All @@ -316,6 +329,21 @@ describe('routes', () => {

expect(route.snapshot.queryParams).toEqual(expectedQueryParams);
expect(queryParams).toEqual(expectedQueryParams);
expect(route.snapshot.queryParamMap.get('a')).toBe('a2');
expect(route.snapshot.queryParamMap.get('b')).toBe('b1');
});

it('should set a query param in the map too, even if the map has already been read before', () => {
const route = stubRoute();

expect(route.snapshot.queryParamMap.get('a')).toBeNull();

const expectedQueryParams = { a: 'a2' };

route.setQueryParam('a', 'a2');

expect(route.snapshot.queryParams).toEqual(expectedQueryParams);
expect(route.snapshot.queryParamMap.get('a')).toBe('a2');
});

it('should set a datum', () => {
Expand Down
12 changes: 11 additions & 1 deletion projects/ngx-speculoos/src/lib/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActivatedRoute, ActivatedRouteSnapshot, convertToParamMap, Data, Params, Route, UrlSegment } from '@angular/router';
import { ActivatedRoute, ActivatedRouteSnapshot, convertToParamMap, Data, ParamMap, Params, Route, UrlSegment } from '@angular/router';
import { BehaviorSubject, map, Observable } from 'rxjs';
import { Type } from '@angular/core';

Expand Down Expand Up @@ -243,6 +243,14 @@ class ActivatedRouteSnapshotStub extends ActivatedRouteSnapshot {
this._routeConfig = route;
}

get paramMap(): ParamMap {
return convertToParamMap(this.params);
}

get queryParamMap(): ParamMap {
return convertToParamMap(this.queryParams);
}

constructor() {
super();
this._root = this;
Expand All @@ -262,6 +270,8 @@ class ActivatedRouteSnapshotStub extends ActivatedRouteSnapshot {
* - when changing the params, query params, fragment, etc., their associated observable emits unconditionally, instead of
* first checking if the value is actually different from before. It's thus the responsibility of the tester to not
* change the values if they're the same as before.
* - the params, paramMap, queryParams and queryParamMap objects of the route snapshot change when params or query params are set
* on the stub route. So if the code keeps a reference to params or paramMaps, it won't see the changes.
*/
export class ActivatedRouteStub extends ActivatedRoute {
private _firstChild: ActivatedRouteStub | null;
Expand Down

0 comments on commit c7859a1

Please sign in to comment.