Skip to content

Commit 2d1d981

Browse files
committed
angular router handler
1 parent 72641c1 commit 2d1d981

File tree

6 files changed

+152
-5
lines changed

6 files changed

+152
-5
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ Basic input/output example:
1919

2020
![Basic](./assets/component-example.png)
2121

22+
With angular router use (built in):
23+
![Angular router](./assets/component-example-with-angular-router.png)
24+
2225
With custom event bus service dependency handler:
2326

2427
![With custom event service dependency handler](./assets/component-example-2.png)
Loading

spec/fixtures/components/home-page/home-page.component.spec.expected.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
22
import { HomePageComponent } from './home-page.component';
3-
import { Router, ActivatedRoute } from '@angular/router';
3+
import { ActivatedRoute, ParamMap, Router, RouterEvent } from '@angular/router';
4+
import { Observable, ReplaySubject } from 'rxjs';
45

56
describe('HomePageComponent', () => {
67
let component: HomePageComponent;
78
let fixture: ComponentFixture<HomePageComponent>;
89
let fakeRouter: jasmine.SpyObj<Router>;
10+
let routerEventsSubject: ReplaySubject<RouterEvent>;
911
let fakeRoute: jasmine.SpyObj<ActivatedRoute>;
12+
let routeParams: { [prop: string]: string };
13+
let routeParamMap: jasmine.SpyObj<ParamMap>;
14+
let routeParamsSubject: ReplaySubject<ParamMap>;
15+
let queryRouteParams: { [prop: string]: string };
16+
let routeQueryParamMap: jasmine.SpyObj<ParamMap>;
17+
let queryRouteParamsSubject: ReplaySubject<ParamMap>;
1018
let fakeWindow: jasmine.SpyObj<Window>;
1119

1220
beforeEach(waitForAsync(() => {
1321
fakeRouter = jasmine.createSpyObj<Router>('Router', ['navigate']);
22+
routerEventsSubject = new ReplaySubject<RouterEvent>(1);
1423
fakeRoute = {} as jasmine.SpyObj<ActivatedRoute>;
24+
routeParams = {};
25+
routeParamMap = jasmine.createSpyObj<ParamMap>('ParamMap', ['get', 'has']);
26+
routeParamMap.get.and.callFake((k) => routeParams[k]);
27+
routeParamMap.has.and.callFake((k) => !!routeParams[k]);
28+
routeParamsSubject = (fakeRoute as { paramMap: Observable<ParamMap> }).paramMap = new ReplaySubject<ParamMap>(1);
29+
queryRouteParams = {};
30+
routeQueryParamMap = jasmine.createSpyObj<ParamMap>('ParamMap', ['get', 'has']);
31+
routeQueryParamMap.get.and.callFake((k) => queryRouteParams[k]);
32+
routeQueryParamMap.has.and.callFake((k) => !!queryRouteParams[k]);
33+
queryRouteParamsSubject = (fakeRoute as { queryParamMap: Observable<ParamMap> }).queryParamMap = new ReplaySubject<ParamMap>(1);
1534
fakeWindow = {} as jasmine.SpyObj<Window>;
1635

1736
TestBed.configureTestingModule({

spec/fixtures/components/home-page/home-page.component.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Component, Inject, OnInit } from '@angular/core';
2-
import { Router, ActivatedRoute } from '@angular/router';
2+
import { Router, ActivatedRoute, NavigationStart } from '@angular/router';
3+
import { take, filter } from 'rxjs/operators';
34

45
@Component({
56
selector: 'app-name',
@@ -14,10 +15,28 @@ export class HomePageComponent implements OnInit {
1415
) { }
1516

1617
ngOnInit(): void {
18+
const value = this.route.snapshot.queryParams['value'];
19+
console.info(value);
20+
1721
this.route.paramMap.subscribe(params => {
1822
if (params.has('type') && params.get('type') === 'user') {
1923
this.router.navigate(['home', 'user']);
2024
}
2125
});
26+
27+
this.route.queryParamMap.subscribe(params => {
28+
if (params.has('type') && params.get('type') === 'user') {
29+
this.router.navigate(['home', 'user']);
30+
}
31+
});
32+
33+
this.router.events
34+
.pipe(
35+
take(1),
36+
filter(ev => ev instanceof NavigationStart)
37+
)
38+
.subscribe(()=>{
39+
console.info('nav start event')
40+
});
2241
}
2342
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
2+
import { DependencyHandler } from '../model';
3+
import defaultDependencyHandler from './default.handler';
4+
5+
export default {
6+
run(result, dep, options) {
7+
defaultDependencyHandler.run(result, dep, options);
8+
9+
// extra subject to support router events if property is used
10+
if (dep.type === 'Router' && options.sourceCode.includes(`${dep.name}.events`)) {
11+
result.imports.push({
12+
path: 'rxjs',
13+
names: ['ReplaySubject']
14+
});
15+
result.imports.push({
16+
path: '@angular/router',
17+
names: ['RouterEvent']
18+
});
19+
20+
result.declarations.push({
21+
name: 'routerEventsSubject',
22+
type: 'ReplaySubject<RouterEvent>'
23+
});
24+
25+
result.initializers.push({
26+
name: 'routerEventsSubject',
27+
value: 'new ReplaySubject<RouterEvent>(1)'
28+
});
29+
}
30+
31+
if (dep.type === 'ActivatedRoute') {
32+
const usesParamMap = options.sourceCode.includes(`${dep.name}.paramMap`);
33+
const usesQueryParamMap = options.sourceCode.includes(`${dep.name}.queryParamMap`);
34+
35+
// param map mock
36+
if (usesParamMap || usesQueryParamMap) {
37+
result.imports.push({
38+
path: '@angular/router',
39+
names: ['ParamMap']
40+
});
41+
42+
result.imports.push({
43+
path: 'rxjs',
44+
names: ['Observable']
45+
});
46+
}
47+
const variants: [apiName: string, varName: string, mapName: string][] = [];
48+
if (usesParamMap) {
49+
variants.push(['paramMap', 'routeParams', 'routeParamMap']);
50+
}
51+
if (usesQueryParamMap) {
52+
variants.push(['queryParamMap', 'queryRouteParams', 'routeQueryParamMap']);
53+
}
54+
55+
variants.forEach(([apiName, varName, mapName]) => {
56+
result.declarations.push({
57+
name: varName,
58+
type: '{ [prop: string]: string }'
59+
});
60+
61+
result.initializers.push({
62+
name: varName,
63+
value: '{}'
64+
});
65+
66+
result.declarations.push({
67+
name: `${mapName}`,
68+
type: 'jasmine.SpyObj<ParamMap>'
69+
});
70+
71+
result.initializers.push({
72+
name: `${mapName}`,
73+
value: `jasmine.createSpyObj<ParamMap>(${options.quoteSymbol}ParamMap${options.quoteSymbol}, [${options.quoteSymbol}get${options.quoteSymbol}, ${options.quoteSymbol}has${options.quoteSymbol}])`
74+
});
75+
76+
result.initializers.push({
77+
value: `${mapName}.get.and.callFake((k) => ${varName}[k])`
78+
});
79+
80+
result.initializers.push({
81+
value: `${mapName}.has.and.callFake((k) => !!${varName}[k])`
82+
});
83+
84+
result.declarations.push({
85+
name: `${varName}Subject`,
86+
type: 'ReplaySubject<ParamMap>'
87+
});
88+
89+
result.initializers.push({
90+
// cast below makes sure that typescript is checking asignment
91+
// this is basically a workaround for readonly properties
92+
name: `${varName}Subject`,
93+
value: `(${options.variableName} as { ${apiName}: Observable<ParamMap> }).${apiName} = new ReplaySubject<ParamMap>(1)`
94+
});
95+
});
96+
97+
}
98+
},
99+
100+
test(dep) {
101+
return dep.type === 'Router' || dep.type === 'ActivatedRoute';
102+
}
103+
} as DependencyHandler;

src/dependency-handlers/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import handler from './default.handler';
21
import { DependencyHandler } from '../model';
2+
import _defaultDependencyHandler from './default.handler';
3+
import _angularRouterHandler from './angular.router.handler';
34

4-
export const defaultDependencyHandler: typeof handler = handler;
5+
export const defaultDependencyHandler: DependencyHandler = _defaultDependencyHandler;
6+
export const angularRouterHandler: DependencyHandler = _angularRouterHandler;
57

68
export const dependencyHandlers: DependencyHandler[] = [
7-
handler
9+
angularRouterHandler,
10+
defaultDependencyHandler,
811
];

0 commit comments

Comments
 (0)