@coolon/angular-ui-powerups 0.0.23 → 0.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/jest.config.js +9 -0
- package/ng-package.json +13 -0
- package/package.json +12 -14
- package/schematics/ng-add/index.ts +10 -0
- package/schematics/place-routes/index.ts +152 -0
- package/schematics/place-routes/place-def.ts +93 -0
- package/schematics/place-routes/scanner.ts +145 -0
- package/schematics/place-routes/schema.ts +7 -0
- package/src/lib/angular-ui-powerups.module.spec.ts +14 -0
- package/src/lib/angular-ui-powerups.module.ts +49 -0
- package/{lib/animations/index.d.ts → src/lib/animations/index.ts} +1 -0
- package/src/lib/animations/slideDownAndUpAnimation.ts +39 -0
- package/src/lib/components/disposable-component.ts +9 -0
- package/src/lib/components/forms/abstract-input.component.ts +108 -0
- package/src/lib/components/forms/number-input/number-input.component.html +15 -0
- package/src/lib/components/forms/number-input/number-input.component.scss +3 -0
- package/src/lib/components/forms/number-input/number-input.component.spec.ts +25 -0
- package/src/lib/components/forms/number-input/number-input.component.ts +19 -0
- package/src/lib/components/forms/password-input/password-input.component.html +16 -0
- package/src/lib/components/forms/password-input/password-input.component.scss +3 -0
- package/src/lib/components/forms/password-input/password-input.component.spec.ts +25 -0
- package/src/lib/components/forms/password-input/password-input.component.ts +18 -0
- package/src/lib/components/forms/scss/mixins.scss +16 -0
- package/src/lib/components/forms/select-input/select-input.component.html +16 -0
- package/src/lib/components/forms/select-input/select-input.component.scss +7 -0
- package/src/lib/components/forms/select-input/select-input.component.spec.ts +25 -0
- package/src/lib/components/forms/select-input/select-input.component.ts +31 -0
- package/src/lib/components/forms/string-input/string-input.component.html +15 -0
- package/src/lib/components/forms/string-input/string-input.component.scss +3 -0
- package/src/lib/components/forms/string-input/string-input.component.spec.ts +25 -0
- package/src/lib/components/forms/string-input/string-input.component.ts +18 -0
- package/{lib/components/index.d.ts → src/lib/components/index.ts} +1 -0
- package/src/lib/components/resize-aware.component.ts +52 -0
- package/src/lib/components/types/bounds.class.ts +25 -0
- package/src/lib/components/types/dimension.class.ts +3 -0
- package/src/lib/components/types/point.class.ts +13 -0
- package/src/lib/directives/command.directive.spec.ts +73 -0
- package/src/lib/directives/command.directive.ts +81 -0
- package/src/lib/directives/focus-binding.directive.spec.ts +8 -0
- package/src/lib/directives/focus-binding.directive.ts +108 -0
- package/{lib/directives/index.d.ts → src/lib/directives/index.ts} +1 -0
- package/src/lib/interaction/abstract-activity.ts +57 -0
- package/src/lib/interaction/activity.ts +17 -0
- package/src/lib/interaction/alert.ts +6 -0
- package/src/lib/interaction/components/activity-container/activity-container.component.html +8 -0
- package/src/lib/interaction/components/activity-container/activity-container.component.scss +3 -0
- package/src/lib/interaction/components/activity-container/activity-container.component.spec.ts +25 -0
- package/src/lib/interaction/components/activity-container/activity-container.component.ts +93 -0
- package/{lib/interaction/components/activity-outlet/activity-outlet-context.d.ts → src/lib/interaction/components/activity-outlet/activity-outlet-context.ts} +7 -5
- package/src/lib/interaction/components/activity-outlet/activity-outlet.component.scss +3 -0
- package/src/lib/interaction/components/activity-outlet/activity-outlet.component.spec.ts +25 -0
- package/src/lib/interaction/components/activity-outlet/activity-outlet.component.ts +96 -0
- package/src/lib/interaction/components/alert-dialog/alert-dialog.component.html +7 -0
- package/src/lib/interaction/components/alert-dialog/alert-dialog.component.scss +7 -0
- package/src/lib/interaction/components/alert-dialog/alert-dialog.component.spec.ts +25 -0
- package/src/lib/interaction/components/alert-dialog/alert-dialog.component.ts +20 -0
- package/src/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.html +9 -0
- package/src/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.scss +8 -0
- package/src/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.spec.ts +25 -0
- package/src/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.ts +26 -0
- package/{lib/interaction/confirm.d.ts → src/lib/interaction/confirm.ts} +3 -2
- package/{lib/interaction/index.d.ts → src/lib/interaction/index.ts} +1 -0
- package/src/lib/interaction/interaction-context.ts +16 -0
- package/src/lib/interaction/interaction.module.ts +35 -0
- package/src/lib/interaction/result.ts +16 -0
- package/src/lib/interaction/services/dialog-interaction.service.scss +11 -0
- package/src/lib/interaction/services/dialog-interaction.service.spec.ts +12 -0
- package/src/lib/interaction/services/dialog-interaction.service.ts +63 -0
- package/src/lib/place/abstract-place-component.ts +33 -0
- package/src/lib/place/components/busy-overlay/busy-overlay.component.html +4 -0
- package/src/lib/place/components/busy-overlay/busy-overlay.component.scss +27 -0
- package/src/lib/place/components/busy-overlay/busy-overlay.component.spec.ts +25 -0
- package/src/lib/place/components/busy-overlay/busy-overlay.component.ts +23 -0
- package/src/lib/place/components/empty-state/empty-state.component.scss +15 -0
- package/src/lib/place/components/empty-state/empty-state.component.spec.ts +25 -0
- package/src/lib/place/components/empty-state/empty-state.component.ts +19 -0
- package/src/lib/place/components/place-outlet/place-outlet.component.html +7 -0
- package/src/lib/place/components/place-outlet/place-outlet.component.scss +12 -0
- package/src/lib/place/components/place-outlet/place-outlet.component.spec.ts +25 -0
- package/src/lib/place/components/place-outlet/place-outlet.component.ts +37 -0
- package/src/lib/place/components/place-outlet/route-animations.ts +30 -0
- package/src/lib/place/components/tabbed-place-navigation/tabbed-place-navigation.component.html +7 -0
- package/src/lib/place/components/tabbed-place-navigation/tabbed-place-navigation.component.scss +100 -0
- package/src/lib/place/components/tabbed-place-navigation/tabbed-place-navigation.component.spec.ts +25 -0
- package/src/lib/place/components/tabbed-place-navigation/tabbed-place-navigation.component.ts +30 -0
- package/src/lib/place/guards/leave-place.guard.spec.ts +15 -0
- package/src/lib/place/guards/leave-place.guard.ts +18 -0
- package/src/lib/place/guards/place-route.guard.ts +37 -0
- package/{lib/place/index.d.ts → src/lib/place/index.ts} +2 -1
- package/src/lib/place/place-component.ts +6 -0
- package/src/lib/place/place.decorator.ts +40 -0
- package/src/lib/place/place.module.ts +44 -0
- package/src/lib/place/place.service.spec.ts +12 -0
- package/src/lib/place/place.service.ts +29 -0
- package/src/lib/place/route/json-types.ts +5 -0
- package/src/lib/place/route/parameter-decorators.ts +99 -0
- package/src/lib/place/route/parameter-definition.class.ts +12 -0
- package/{lib/place/route/parameter-sanitiser.d.ts → src/lib/place/route/parameter-sanitiser.ts} +1 -1
- package/src/lib/place/route/parameter-type.ts +31 -0
- package/src/lib/place/route/route-parameter-bindings.ts +140 -0
- package/src/lib/place/route/route-parameter-sanitiser.ts +135 -0
- package/src/test-setup.ts +1 -0
- package/tsconfig.json +7 -0
- package/tsconfig.lib.json +20 -0
- package/tsconfig.schematics.json +35 -0
- package/tsconfig.spec.json +10 -0
- package/tslint.json +7 -0
- package/bundles/coolon-angular-ui-powerups.umd.js +0 -3908
- package/bundles/coolon-angular-ui-powerups.umd.js.map +0 -1
- package/bundles/coolon-angular-ui-powerups.umd.min.js +0 -2
- package/bundles/coolon-angular-ui-powerups.umd.min.js.map +0 -1
- package/coolon-angular-ui-powerups.d.ts +0 -30
- package/coolon-angular-ui-powerups.metadata.json +0 -1
- package/esm2015/coolon-angular-ui-powerups.js +0 -33
- package/esm2015/index.js +0 -6
- package/esm2015/lib/angular-ui-powerups.module.js +0 -54
- package/esm2015/lib/animations/index.js +0 -9
- package/esm2015/lib/animations/slideDownAndUpAnimation.js +0 -30
- package/esm2015/lib/components/disposable-component.js +0 -14
- package/esm2015/lib/components/forms/abstract-input.component.js +0 -183
- package/esm2015/lib/components/forms/number-input/number-input.component.js +0 -26
- package/esm2015/lib/components/forms/password-input/password-input.component.js +0 -26
- package/esm2015/lib/components/forms/select-input/select-input.component.js +0 -60
- package/esm2015/lib/components/forms/string-input/string-input.component.js +0 -26
- package/esm2015/lib/components/index.js +0 -18
- package/esm2015/lib/components/resize-aware.component.js +0 -96
- package/esm2015/lib/components/types/bounds.class.js +0 -56
- package/esm2015/lib/components/types/dimension.class.js +0 -21
- package/esm2015/lib/components/types/point.class.js +0 -36
- package/esm2015/lib/directives/command.directive.js +0 -135
- package/esm2015/lib/directives/focus-binding.directive.js +0 -179
- package/esm2015/lib/directives/index.js +0 -10
- package/esm2015/lib/interaction/abstract-activity.js +0 -129
- package/esm2015/lib/interaction/activity.js +0 -27
- package/esm2015/lib/interaction/alert.js +0 -23
- package/esm2015/lib/interaction/components/activity-container/activity-container.component.js +0 -144
- package/esm2015/lib/interaction/components/activity-outlet/activity-outlet-context.js +0 -22
- package/esm2015/lib/interaction/components/activity-outlet/activity-outlet.component.js +0 -169
- package/esm2015/lib/interaction/components/alert-dialog/alert-dialog.component.js +0 -37
- package/esm2015/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.js +0 -46
- package/esm2015/lib/interaction/confirm.js +0 -17
- package/esm2015/lib/interaction/index.js +0 -21
- package/esm2015/lib/interaction/interaction-context.js +0 -35
- package/esm2015/lib/interaction/interaction.module.js +0 -41
- package/esm2015/lib/interaction/result.js +0 -24
- package/esm2015/lib/interaction/services/dialog-interaction.service.js +0 -114
- package/esm2015/lib/place/abstract-place-component.js +0 -59
- package/esm2015/lib/place/components/busy-overlay/busy-overlay.component.js +0 -44
- package/esm2015/lib/place/components/empty-state/empty-state.component.js +0 -28
- package/esm2015/lib/place/components/nav/nav.component.js +0 -31
- package/esm2015/lib/place/components/place-outlet/place-outlet.component.js +0 -67
- package/esm2015/lib/place/components/place-outlet/route-animations.js +0 -35
- package/esm2015/lib/place/guards/leave-place.guard.js +0 -52
- package/esm2015/lib/place/guards/place-route.guard.js +0 -74
- package/esm2015/lib/place/index.js +0 -27
- package/esm2015/lib/place/place-component.js +0 -17
- package/esm2015/lib/place/place.decorator.js +0 -72
- package/esm2015/lib/place/place.module.js +0 -49
- package/esm2015/lib/place/place.service.js +0 -61
- package/esm2015/lib/place/route/json-types.js +0 -13
- package/esm2015/lib/place/route/parameter-decorators.js +0 -178
- package/esm2015/lib/place/route/parameter-definition.class.js +0 -24
- package/esm2015/lib/place/route/parameter-sanitiser.js +0 -17
- package/esm2015/lib/place/route/parameter-type.js +0 -43
- package/esm2015/lib/place/route/route-parameter-bindings.js +0 -294
- package/esm2015/lib/place/route/route-parameter-sanitiser.js +0 -280
- package/esm2015/public_api.js +0 -11
- package/esm5/coolon-angular-ui-powerups.js +0 -33
- package/esm5/index.js +0 -6
- package/esm5/lib/angular-ui-powerups.module.js +0 -58
- package/esm5/lib/animations/index.js +0 -9
- package/esm5/lib/animations/slideDownAndUpAnimation.js +0 -30
- package/esm5/lib/components/disposable-component.js +0 -24
- package/esm5/lib/components/forms/abstract-input.component.js +0 -232
- package/esm5/lib/components/forms/number-input/number-input.component.js +0 -33
- package/esm5/lib/components/forms/password-input/password-input.component.js +0 -33
- package/esm5/lib/components/forms/select-input/select-input.component.js +0 -67
- package/esm5/lib/components/forms/string-input/string-input.component.js +0 -33
- package/esm5/lib/components/index.js +0 -18
- package/esm5/lib/components/resize-aware.component.js +0 -124
- package/esm5/lib/components/types/bounds.class.js +0 -59
- package/esm5/lib/components/types/dimension.class.js +0 -19
- package/esm5/lib/components/types/point.class.js +0 -43
- package/esm5/lib/directives/command.directive.js +0 -155
- package/esm5/lib/directives/focus-binding.directive.js +0 -231
- package/esm5/lib/directives/index.js +0 -10
- package/esm5/lib/interaction/abstract-activity.js +0 -173
- package/esm5/lib/interaction/activity.js +0 -27
- package/esm5/lib/interaction/alert.js +0 -23
- package/esm5/lib/interaction/components/activity-container/activity-container.component.js +0 -181
- package/esm5/lib/interaction/components/activity-outlet/activity-outlet-context.js +0 -22
- package/esm5/lib/interaction/components/activity-outlet/activity-outlet.component.js +0 -206
- package/esm5/lib/interaction/components/alert-dialog/alert-dialog.component.js +0 -42
- package/esm5/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.js +0 -54
- package/esm5/lib/interaction/confirm.js +0 -17
- package/esm5/lib/interaction/index.js +0 -21
- package/esm5/lib/interaction/interaction-context.js +0 -45
- package/esm5/lib/interaction/interaction.module.js +0 -45
- package/esm5/lib/interaction/result.js +0 -24
- package/esm5/lib/interaction/services/dialog-interaction.service.js +0 -140
- package/esm5/lib/place/abstract-place-component.js +0 -66
- package/esm5/lib/place/components/busy-overlay/busy-overlay.component.js +0 -49
- package/esm5/lib/place/components/empty-state/empty-state.component.js +0 -29
- package/esm5/lib/place/components/nav/nav.component.js +0 -36
- package/esm5/lib/place/components/place-outlet/place-outlet.component.js +0 -81
- package/esm5/lib/place/components/place-outlet/route-animations.js +0 -35
- package/esm5/lib/place/guards/leave-place.guard.js +0 -57
- package/esm5/lib/place/guards/place-route.guard.js +0 -78
- package/esm5/lib/place/index.js +0 -27
- package/esm5/lib/place/place-component.js +0 -17
- package/esm5/lib/place/place.decorator.js +0 -69
- package/esm5/lib/place/place.module.js +0 -53
- package/esm5/lib/place/place.service.js +0 -75
- package/esm5/lib/place/route/json-types.js +0 -13
- package/esm5/lib/place/route/parameter-decorators.js +0 -178
- package/esm5/lib/place/route/parameter-definition.class.js +0 -24
- package/esm5/lib/place/route/parameter-sanitiser.js +0 -17
- package/esm5/lib/place/route/parameter-type.js +0 -65
- package/esm5/lib/place/route/route-parameter-bindings.js +0 -339
- package/esm5/lib/place/route/route-parameter-sanitiser.js +0 -395
- package/esm5/public_api.js +0 -11
- package/fesm2015/coolon-angular-ui-powerups.js +0 -3129
- package/fesm2015/coolon-angular-ui-powerups.js.map +0 -1
- package/fesm5/coolon-angular-ui-powerups.js +0 -3681
- package/fesm5/coolon-angular-ui-powerups.js.map +0 -1
- package/lib/angular-ui-powerups.module.d.ts +0 -2
- package/lib/animations/slideDownAndUpAnimation.d.ts +0 -5
- package/lib/components/disposable-component.d.ts +0 -5
- package/lib/components/forms/abstract-input.component.d.ts +0 -35
- package/lib/components/forms/number-input/number-input.component.d.ts +0 -6
- package/lib/components/forms/password-input/password-input.component.d.ts +0 -6
- package/lib/components/forms/select-input/select-input.component.d.ts +0 -11
- package/lib/components/forms/string-input/string-input.component.d.ts +0 -6
- package/lib/components/resize-aware.component.d.ts +0 -22
- package/lib/components/types/bounds.class.d.ts +0 -16
- package/lib/components/types/dimension.class.d.ts +0 -5
- package/lib/components/types/point.class.d.ts +0 -10
- package/lib/directives/command.directive.d.ts +0 -14
- package/lib/directives/focus-binding.directive.d.ts +0 -22
- package/lib/interaction/abstract-activity.d.ts +0 -17
- package/lib/interaction/activity.d.ts +0 -11
- package/lib/interaction/alert.d.ts +0 -5
- package/lib/interaction/components/activity-container/activity-container.component.d.ts +0 -32
- package/lib/interaction/components/activity-outlet/activity-outlet.component.d.ts +0 -30
- package/lib/interaction/components/alert-dialog/alert-dialog.component.d.ts +0 -8
- package/lib/interaction/components/confirmation-dialog/confirmation-dialog.component.d.ts +0 -9
- package/lib/interaction/interaction-context.d.ts +0 -11
- package/lib/interaction/interaction.module.d.ts +0 -2
- package/lib/interaction/result.d.ts +0 -8
- package/lib/interaction/services/dialog-interaction.service.d.ts +0 -14
- package/lib/place/abstract-place-component.d.ts +0 -13
- package/lib/place/components/busy-overlay/busy-overlay.component.d.ts +0 -8
- package/lib/place/components/empty-state/empty-state.component.d.ts +0 -5
- package/lib/place/components/nav/nav.component.d.ts +0 -7
- package/lib/place/components/place-outlet/place-outlet.component.d.ts +0 -13
- package/lib/place/components/place-outlet/route-animations.d.ts +0 -1
- package/lib/place/guards/leave-place.guard.d.ts +0 -10
- package/lib/place/guards/place-route.guard.d.ts +0 -9
- package/lib/place/place-component.d.ts +0 -5
- package/lib/place/place.decorator.d.ts +0 -20
- package/lib/place/place.module.d.ts +0 -2
- package/lib/place/place.service.d.ts +0 -8
- package/lib/place/route/json-types.d.ts +0 -7
- package/lib/place/route/parameter-decorators.d.ts +0 -26
- package/lib/place/route/parameter-definition.class.d.ts +0 -11
- package/lib/place/route/parameter-type.d.ts +0 -16
- package/lib/place/route/route-parameter-bindings.d.ts +0 -18
- package/lib/place/route/route-parameter-sanitiser.d.ts +0 -42
- package/schematics/ng-add/index.d.ts +0 -2
- package/schematics/ng-add/index.js +0 -12
- package/schematics/ng-add/index.js.map +0 -1
- package/schematics/place-routes/index.d.ts +0 -3
- package/schematics/place-routes/index.js +0 -114
- package/schematics/place-routes/index.js.map +0 -1
- package/schematics/place-routes/place-def.d.ts +0 -34
- package/schematics/place-routes/place-def.js +0 -84
- package/schematics/place-routes/place-def.js.map +0 -1
- package/schematics/place-routes/scanner.d.ts +0 -26
- package/schematics/place-routes/scanner.js +0 -125
- package/schematics/place-routes/scanner.js.map +0 -1
- package/schematics/place-routes/schema.d.ts +0 -7
- package/schematics/place-routes/schema.js +0 -3
- package/schematics/place-routes/schema.js.map +0 -1
- /package/{index.d.ts → src/index.ts} +0 -0
- /package/{public_api.d.ts → src/public_api.ts} +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Injectable, Injector } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
ActivatedRouteSnapshot,
|
|
4
|
+
CanActivate,
|
|
5
|
+
CanDeactivate,
|
|
6
|
+
Router,
|
|
7
|
+
RouterStateSnapshot,
|
|
8
|
+
UrlTree
|
|
9
|
+
} from '@angular/router';
|
|
10
|
+
import { Trace } from '@coolon/pectin';
|
|
11
|
+
import { Klass } from '@coolon/typescript-utilities';
|
|
12
|
+
import { Observable } from 'rxjs';
|
|
13
|
+
import { PlaceComponent } from '../place-component';
|
|
14
|
+
import { RouteParameterBindings } from '../route/route-parameter-bindings';
|
|
15
|
+
import { RouteParameterSanitiser } from '../route/route-parameter-sanitiser';
|
|
16
|
+
|
|
17
|
+
@Injectable({
|
|
18
|
+
providedIn: 'root'
|
|
19
|
+
})
|
|
20
|
+
export class PlaceRouteGuard implements CanActivate {
|
|
21
|
+
|
|
22
|
+
constructor(private router: Router, private injector: Injector) {
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Trace()
|
|
26
|
+
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
|
27
|
+
const routeParameters = Klass.get(route.component).getMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY);
|
|
28
|
+
const sanitiser = new RouteParameterSanitiser(routeParameters, route, this.injector);
|
|
29
|
+
return sanitiser.sanitise().then(proceed => {
|
|
30
|
+
if (!proceed) {
|
|
31
|
+
sanitiser.redirect(this.router);
|
|
32
|
+
}
|
|
33
|
+
return proceed;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Automatically generated by barrelsby.
|
|
3
3
|
*/
|
|
4
|
+
|
|
4
5
|
export * from "./abstract-place-component";
|
|
5
6
|
export * from "./place-component";
|
|
6
7
|
export * from "./place.decorator";
|
|
@@ -8,9 +9,9 @@ export * from "./place.module";
|
|
|
8
9
|
export * from "./place.service";
|
|
9
10
|
export * from "./components/busy-overlay/busy-overlay.component";
|
|
10
11
|
export * from "./components/empty-state/empty-state.component";
|
|
11
|
-
export * from "./components/nav/nav.component";
|
|
12
12
|
export * from "./components/place-outlet/place-outlet.component";
|
|
13
13
|
export * from "./components/place-outlet/route-animations";
|
|
14
|
+
export * from "./components/tabbed-place-navigation/tabbed-place-navigation.component";
|
|
14
15
|
export * from "./guards/leave-place.guard";
|
|
15
16
|
export * from "./guards/place-route.guard";
|
|
16
17
|
export * from "./route/json-types";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { assert, Constructable } from '@coolon/pectin';
|
|
2
|
+
import * as _ from 'lodash';
|
|
3
|
+
import 'reflect-metadata';
|
|
4
|
+
import { PlaceComponent } from './place-component';
|
|
5
|
+
|
|
6
|
+
export function Place(options: Place.Options) {
|
|
7
|
+
return (target: Function) => {
|
|
8
|
+
Reflect.defineMetadata('place.options', options, target);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export namespace Place {
|
|
13
|
+
export interface Options {
|
|
14
|
+
path: string,
|
|
15
|
+
title: string,
|
|
16
|
+
order: number;
|
|
17
|
+
authenticate?: boolean // defaults to true
|
|
18
|
+
// childOf?: Constructable<PlaceComponent>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class Descriptor {
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly authenticate: boolean;
|
|
24
|
+
readonly title: string;
|
|
25
|
+
readonly order: number;
|
|
26
|
+
|
|
27
|
+
constructor(placeClass: Constructable<PlaceComponent>, options: Options) {
|
|
28
|
+
assert.notNil(options);
|
|
29
|
+
this.path = options.path;
|
|
30
|
+
this.title = options.title;
|
|
31
|
+
this.order = options.order;
|
|
32
|
+
this.authenticate = _.isNil(options.authenticate) || options.authenticate;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getDescriptor(klass: Constructable<PlaceComponent>): Descriptor {
|
|
37
|
+
const placeOptions = Reflect.getMetadata('place.options', klass);
|
|
38
|
+
return new Descriptor(klass, placeOptions);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { NgModule } from '@angular/core';
|
|
3
|
+
import { MatProgressSpinnerModule } from '@angular/material';
|
|
4
|
+
import { MatListModule } from '@angular/material/list';
|
|
5
|
+
import { MatTabsModule } from '@angular/material/tabs';
|
|
6
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
7
|
+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
8
|
+
import { RouterModule } from '@angular/router';
|
|
9
|
+
import { BusyOverlayComponent } from './components/busy-overlay/busy-overlay.component';
|
|
10
|
+
import { EmptyStateComponent } from './components/empty-state/empty-state.component';
|
|
11
|
+
import { PlaceOutletComponent } from './components/place-outlet/place-outlet.component';
|
|
12
|
+
import { TabbedPlaceNavigationComponent } from './components/tabbed-place-navigation/tabbed-place-navigation.component';
|
|
13
|
+
import { PlaceService } from './place.service';
|
|
14
|
+
|
|
15
|
+
@NgModule({
|
|
16
|
+
imports: [
|
|
17
|
+
CommonModule,
|
|
18
|
+
BrowserAnimationsModule,
|
|
19
|
+
MatProgressSpinnerModule,
|
|
20
|
+
RouterModule,
|
|
21
|
+
MatTabsModule,
|
|
22
|
+
MatListModule,
|
|
23
|
+
MatTooltipModule
|
|
24
|
+
],
|
|
25
|
+
declarations: [
|
|
26
|
+
EmptyStateComponent,
|
|
27
|
+
BusyOverlayComponent,
|
|
28
|
+
PlaceOutletComponent,
|
|
29
|
+
TabbedPlaceNavigationComponent
|
|
30
|
+
],
|
|
31
|
+
entryComponents: [BusyOverlayComponent],
|
|
32
|
+
exports: [
|
|
33
|
+
BusyOverlayComponent,
|
|
34
|
+
PlaceOutletComponent,
|
|
35
|
+
EmptyStateComponent,
|
|
36
|
+
TabbedPlaceNavigationComponent
|
|
37
|
+
],
|
|
38
|
+
providers: [
|
|
39
|
+
PlaceService
|
|
40
|
+
]
|
|
41
|
+
})
|
|
42
|
+
export class PlaceModule {
|
|
43
|
+
|
|
44
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { PlaceService } from './place.service';
|
|
4
|
+
|
|
5
|
+
describe('PlaceService', () => {
|
|
6
|
+
beforeEach(() => TestBed.configureTestingModule({}));
|
|
7
|
+
|
|
8
|
+
it('should be created', () => {
|
|
9
|
+
const service: PlaceService = TestBed.get(PlaceService);
|
|
10
|
+
expect(service).toBeTruthy();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { MutableListModel, Trace } from '@coolon/pectin';
|
|
3
|
+
import { PlaceComponent } from './place-component';
|
|
4
|
+
|
|
5
|
+
@Injectable({
|
|
6
|
+
providedIn: 'root'
|
|
7
|
+
})
|
|
8
|
+
export class PlaceService {
|
|
9
|
+
|
|
10
|
+
private _activePlaces = new MutableListModel<PlaceComponent>();
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get activePlaces() {
|
|
16
|
+
return this._activePlaces.readonly;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Trace()
|
|
20
|
+
onActivate(place: PlaceComponent) {
|
|
21
|
+
this._activePlaces.push(place);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Trace()
|
|
25
|
+
onDeactivate(place: PlaceComponent) {
|
|
26
|
+
this._activePlaces.remove(place);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { assert, Constructable, UseDelegate } from '@coolon/pectin';
|
|
2
|
+
import { Klass, Opts } from '@coolon/typescript-utilities';
|
|
3
|
+
import { ParameterDefinition } from './parameter-definition.class';
|
|
4
|
+
import { ParameterSanitiser } from './parameter-sanitiser';
|
|
5
|
+
import { ParameterType } from './parameter-type';
|
|
6
|
+
import { RouteParameterBindings } from './route-parameter-bindings';
|
|
7
|
+
|
|
8
|
+
export interface ParameterOptions<T> {
|
|
9
|
+
as?: string;
|
|
10
|
+
sanitiser?: Constructable<ParameterSanitiser<T>>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ListParameterOptions<T> {
|
|
14
|
+
as?: string;
|
|
15
|
+
listSanitiser?: Constructable<ParameterSanitiser<T[]>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function RouteId<T extends string | number>(type: ParameterType<T>, options?: ParameterOptions<T>): PropertyDecorator {
|
|
19
|
+
// assert.isTruthy(type.isValid());
|
|
20
|
+
return (prototype: Object, propertyKey: string | symbol) => {
|
|
21
|
+
const param = createDefinition(propertyKey, type, options, true);
|
|
22
|
+
Klass.get(prototype).addToOwnMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY, param);
|
|
23
|
+
UseDelegate()(prototype, propertyKey);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated use RouteParameter instead.
|
|
29
|
+
* @param type
|
|
30
|
+
* @param options
|
|
31
|
+
*/
|
|
32
|
+
export function QueryParameter<T>(type: ParameterType<T>, options?: ParameterOptions<T>): PropertyDecorator {
|
|
33
|
+
return (prototype: Object, propertyKey: string | symbol) => {
|
|
34
|
+
const param = createDefinition(propertyKey, type, options, false);
|
|
35
|
+
Klass.get(prototype).addToOwnMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY, param);
|
|
36
|
+
UseDelegate()(prototype, propertyKey);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function RouteParameter<T>(type: ParameterType<T>, options?: ParameterOptions<T>): PropertyDecorator {
|
|
41
|
+
// assert.isTruthy(type.isValid());
|
|
42
|
+
return (prototype: Object, propertyKey: string | symbol) => {
|
|
43
|
+
const param = createDefinition(propertyKey, type, options, false);
|
|
44
|
+
Klass.get(prototype).addToOwnMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY, param);
|
|
45
|
+
UseDelegate()(prototype, propertyKey);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @deprecated use RouteParameterList instead
|
|
51
|
+
* @param elementType
|
|
52
|
+
* @param options
|
|
53
|
+
*/
|
|
54
|
+
export function QueryParameterList<E>(elementType: ParameterType<E>, options?: ListParameterOptions<E>): PropertyDecorator {
|
|
55
|
+
// assert.isTruthy(type.isValid());
|
|
56
|
+
return (prototype: Object, propertyKey: string | symbol) => {
|
|
57
|
+
const param = createArrayDefinition(propertyKey, elementType, options);
|
|
58
|
+
Klass.get(prototype).addToOwnMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY, param);
|
|
59
|
+
UseDelegate()(prototype, propertyKey);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function RouteParameterList<E>(elementType: ParameterType<E>, options?: ListParameterOptions<E>): PropertyDecorator {
|
|
64
|
+
// assert.isTruthy(type.isValid());
|
|
65
|
+
return (prototype: Object, propertyKey: string | symbol) => {
|
|
66
|
+
const param = createArrayDefinition(propertyKey, elementType, options);
|
|
67
|
+
Klass.get(prototype).addToOwnMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY, param);
|
|
68
|
+
UseDelegate()(prototype, propertyKey);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function createDefinition<T>(propertyName: symbol | string, type: ParameterType<T>, options: ParameterOptions<T>, routeId: boolean) {
|
|
73
|
+
const opts = Opts.get(options);
|
|
74
|
+
const propertyNameAsString = assert.notNil(propertyName).toString();
|
|
75
|
+
const def: ParameterDefinition<T> = {
|
|
76
|
+
name: opts.getOptional('as', propertyNameAsString),
|
|
77
|
+
propertyName: propertyNameAsString,
|
|
78
|
+
type: assert.notNil(type),
|
|
79
|
+
sanitiser: opts.getOptional('sanitiser', null),
|
|
80
|
+
isRouteId: routeId,
|
|
81
|
+
isArray: false
|
|
82
|
+
};
|
|
83
|
+
return def;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createArrayDefinition<T>(propertyName: symbol | string, type: ParameterType<T>, options: ListParameterOptions<T>) {
|
|
87
|
+
const opts = Opts.get(options);
|
|
88
|
+
const propertyNameAsString = assert.notNil(propertyName).toString();
|
|
89
|
+
|
|
90
|
+
const def: ParameterDefinition<T, T[]> = {
|
|
91
|
+
name: opts.getOptional('as', propertyNameAsString),
|
|
92
|
+
propertyName: propertyNameAsString,
|
|
93
|
+
type: assert.notNil(type),
|
|
94
|
+
sanitiser: opts.getOptional('listSanitiser', null),
|
|
95
|
+
isRouteId: false,
|
|
96
|
+
isArray: true
|
|
97
|
+
};
|
|
98
|
+
return def;
|
|
99
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Constructable } from '@coolon/pectin';
|
|
2
|
+
import { ParameterSanitiser } from './parameter-sanitiser';
|
|
3
|
+
import { ParameterType } from './parameter-type';
|
|
4
|
+
|
|
5
|
+
export interface ParameterDefinition<E,S=E> {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly propertyName: string;
|
|
8
|
+
readonly type: ParameterType<E>;
|
|
9
|
+
readonly sanitiser: Constructable<ParameterSanitiser<S>>;
|
|
10
|
+
readonly isRouteId: boolean;
|
|
11
|
+
readonly isArray: boolean;
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ParameterType<T> {
|
|
2
|
+
format(value: T): string;
|
|
3
|
+
|
|
4
|
+
parse(value: string): T;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export namespace ParameterType {
|
|
8
|
+
|
|
9
|
+
class StringType implements ParameterType<string> {
|
|
10
|
+
format(value: string): string {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
parse(value: string): string {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class NumberType implements ParameterType<number> {
|
|
20
|
+
format(value: number): string {
|
|
21
|
+
return `${value}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
parse(value: string): number {
|
|
25
|
+
return +value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const STRING = new StringType();
|
|
30
|
+
export const NUMBER = new NumberType();
|
|
31
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
2
|
+
import {
|
|
3
|
+
Binding,
|
|
4
|
+
Dsl,
|
|
5
|
+
GarbageCollector, IMutableHasValue,
|
|
6
|
+
MutableValueModel,
|
|
7
|
+
Trace,
|
|
8
|
+
TransactionObserver
|
|
9
|
+
} from '@coolon/pectin';
|
|
10
|
+
import { Klass } from '@coolon/typescript-utilities';
|
|
11
|
+
import { ParameterDefinition } from './parameter-definition.class';
|
|
12
|
+
|
|
13
|
+
interface ResolvedParameter<T> {
|
|
14
|
+
parameterDef: ParameterDefinition<T>;
|
|
15
|
+
resolvedValue: T;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class ResolvedParameters {
|
|
19
|
+
constructor(readonly parameters: ResolvedParameter<any>[]) {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get<T>(parameter: ParameterDefinition<T>): T {
|
|
23
|
+
const resolved = this.parameters.find(p => p.parameterDef === parameter);
|
|
24
|
+
return resolved ? resolved.resolvedValue : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
replace<T>(parameter: ParameterDefinition<T>, value: T): ResolvedParameters {
|
|
28
|
+
return new ResolvedParameters(this.parameters.filter(p => p.parameterDef !== parameter).concat({
|
|
29
|
+
parameterDef: parameter,
|
|
30
|
+
resolvedValue: value
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Trace()
|
|
35
|
+
protected getFormattedRouteId() {
|
|
36
|
+
const routeIdParam = this.getRouteIdParam();
|
|
37
|
+
return routeIdParam ? routeIdParam.parameterDef.type.format(routeIdParam.resolvedValue) : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private getRouteIdParam() {
|
|
41
|
+
return this.parameters.find(p => p.parameterDef.isRouteId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Trace()
|
|
45
|
+
protected getFormattedParameters() {
|
|
46
|
+
const params = {};
|
|
47
|
+
const routeIdParam = this.parameters.filter(p => !p.parameterDef.isRouteId).forEach(p => {
|
|
48
|
+
params[p.parameterDef.name] = p.parameterDef.type.format(p.resolvedValue);
|
|
49
|
+
});
|
|
50
|
+
return params;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
hasRouteId() {
|
|
54
|
+
return !!this.getRouteIdParam();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Trace()
|
|
58
|
+
redirect(previous: ResolvedParameters, router: Router, activatedRoute: ActivatedRoute) {
|
|
59
|
+
const routeId = this.getFormattedRouteId();
|
|
60
|
+
const relativePath = !!previous.hasRouteId() ? '..' : '.';
|
|
61
|
+
const path = routeId ? [relativePath, routeId, this.getFormattedParameters()] : [relativePath, this.getFormattedParameters()];
|
|
62
|
+
router.navigate(path, {
|
|
63
|
+
relativeTo: activatedRoute
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class RouteParameterBindings extends GarbageCollector {
|
|
69
|
+
|
|
70
|
+
public readonly _incomingRouteChange = new MutableValueModel<boolean>(false);
|
|
71
|
+
|
|
72
|
+
private txo: TransactionObserver;
|
|
73
|
+
|
|
74
|
+
private readonly resolvedParameters = new MutableValueModel<ResolvedParameters>();
|
|
75
|
+
|
|
76
|
+
constructor(private readonly component: any, private readonly router: Router, private readonly activatedRoute: ActivatedRoute) {
|
|
77
|
+
super();
|
|
78
|
+
const { chain, valueOf, before, when, after, observe } = Dsl.disposeWith(this);
|
|
79
|
+
this.txo = new TransactionObserver();//.onEvent(e => console.log(e.toString()));
|
|
80
|
+
|
|
81
|
+
const routeParameterDefs: ParameterDefinition<any>[] = Klass.get(component).getMetadataList(RouteParameterBindings.ROUTE_PARAMETER_LIST_KEY);
|
|
82
|
+
routeParameterDefs.forEach(parameter => this.installBinding(parameter));
|
|
83
|
+
|
|
84
|
+
const incomingResolvedRouteParameters = observe.valuesFrom(activatedRoute.paramMap)
|
|
85
|
+
.last()
|
|
86
|
+
.map(params => {
|
|
87
|
+
const result: ResolvedParameter<any>[] = [];
|
|
88
|
+
routeParameterDefs.forEach(parameterDef => {
|
|
89
|
+
let resolvedValue;
|
|
90
|
+
if (parameterDef.isArray) {
|
|
91
|
+
resolvedValue = params.getAll(parameterDef.name).map(v => parameterDef.type.parse(v));
|
|
92
|
+
} else {
|
|
93
|
+
resolvedValue = parameterDef.type.parse(params.get(parameterDef.name));
|
|
94
|
+
}
|
|
95
|
+
result.push({ parameterDef: parameterDef, resolvedValue: resolvedValue });
|
|
96
|
+
|
|
97
|
+
});
|
|
98
|
+
return new ResolvedParameters(result);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
incomingResolvedRouteParameters.subscribe(resolvedParameters => {
|
|
102
|
+
this._incomingRouteChange.value = true;
|
|
103
|
+
this.resolvedParameters.value = resolvedParameters;
|
|
104
|
+
this._incomingRouteChange.value = false;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
when.valueOf(this.resolvedParameters).changes((params, previous) => {
|
|
108
|
+
params.redirect(previous, router, activatedRoute);
|
|
109
|
+
}, { unless: this.incomingRouteChange });
|
|
110
|
+
|
|
111
|
+
this.txo.observe('incomingResolvedRouteParameters', incomingResolvedRouteParameters);
|
|
112
|
+
this.txo.observe('this.routeIsChanging', this.incomingRouteChange);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get incomingRouteChange() {
|
|
116
|
+
return this._incomingRouteChange.readonly;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected installBinding<T>(parameter: ParameterDefinition<T>) {
|
|
120
|
+
const model = this.getModelFor(parameter);
|
|
121
|
+
// this.txo.observe(parameter.propertyName, model);
|
|
122
|
+
this.registerDisposable(new Binding(this.resolvedParameters, model, {
|
|
123
|
+
toRight(parameters: ResolvedParameters): T {
|
|
124
|
+
return parameters.get(parameter);
|
|
125
|
+
},
|
|
126
|
+
toLeft(value: T, currentParameters?: ResolvedParameters): ResolvedParameters {
|
|
127
|
+
return currentParameters.replace(parameter, value);
|
|
128
|
+
}
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
private getModelFor<T>(parameter: ParameterDefinition<T>): IMutableHasValue<T> {
|
|
134
|
+
return this.component[parameter.propertyName];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export namespace RouteParameterBindings {
|
|
139
|
+
export const ROUTE_PARAMETER_LIST_KEY = Symbol('place:route:parameters');
|
|
140
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Injector } from '@angular/core';
|
|
2
|
+
import { ActivatedRouteSnapshot, Router } from '@angular/router';
|
|
3
|
+
import { assert } from '@coolon/pectin';
|
|
4
|
+
import { Memoize } from '@typescript-plus/fast-memoize-decorator/dist/src';
|
|
5
|
+
import { ParameterDefinition } from './parameter-definition.class';
|
|
6
|
+
import { ParameterSanitiser } from './parameter-sanitiser';
|
|
7
|
+
import * as _ from 'lodash';
|
|
8
|
+
|
|
9
|
+
export abstract class Sanitiser<T, S> {
|
|
10
|
+
|
|
11
|
+
private initialValue: S;
|
|
12
|
+
private _sanitisedValue: S;
|
|
13
|
+
|
|
14
|
+
constructor(readonly parameterDef: ParameterDefinition<T>, readonly snapshot:ActivatedRouteSnapshot, readonly sanitiser: ParameterSanitiser<S>) {
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
isRouteId(): boolean {
|
|
18
|
+
return this.parameterDef.isRouteId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get valueChanged():boolean {
|
|
22
|
+
return !_.isNil(this.initialValue) && this.hasChanged(this.initialValue, this.sanitisedValue);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get sanitisedValue():S {
|
|
26
|
+
return this._sanitisedValue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected abstract readFrom(snapshot: ActivatedRouteSnapshot): S;
|
|
30
|
+
protected abstract hasChanged(initialValue:S, sanitisedValue:S): boolean;
|
|
31
|
+
|
|
32
|
+
abstract injectRouteParam(dest: {});
|
|
33
|
+
|
|
34
|
+
async sanitize(): Promise<boolean> {
|
|
35
|
+
if (this.sanitiser) {
|
|
36
|
+
this.initialValue = this.readFrom(this.snapshot);
|
|
37
|
+
this._sanitisedValue = await this.sanitiser.sanitise(this.initialValue);
|
|
38
|
+
if (this.initialValue !== this._sanitisedValue) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export class RouteParameterSanitiser {
|
|
48
|
+
|
|
49
|
+
private readonly routeIdSanitiser: Sanitiser<any, any>;
|
|
50
|
+
private readonly parameterSanitisers: Sanitiser<any, any>[] = [];
|
|
51
|
+
|
|
52
|
+
constructor(readonly parameterDefs: ParameterDefinition<any>[], readonly snapshot: ActivatedRouteSnapshot, readonly injector: Injector) {
|
|
53
|
+
const sanitisers = parameterDefs.map(def => {
|
|
54
|
+
return def.isArray ?
|
|
55
|
+
new Sanitiser.Array(def, snapshot, this.getSanitiser(def)) :
|
|
56
|
+
new Sanitiser.Value(def, snapshot, this.getSanitiser(def));
|
|
57
|
+
});
|
|
58
|
+
this.routeIdSanitiser = sanitisers.find(p => p.isRouteId());
|
|
59
|
+
this.parameterSanitisers = sanitisers.filter(p => !p.isRouteId());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async sanitise(): Promise<boolean> {
|
|
63
|
+
if (this.routeIdSanitiser) {
|
|
64
|
+
await this.routeIdSanitiser.sanitize();
|
|
65
|
+
if (this.routeIdSanitiser.valueChanged) {
|
|
66
|
+
// if the route id isn't ok we redirect immediately as the query params may need to be sanitised in the
|
|
67
|
+
// context of the route id.
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const sanitiser of this.parameterSanitisers) {
|
|
72
|
+
await sanitiser.sanitize();
|
|
73
|
+
}
|
|
74
|
+
return !this.parameterSanitisers.find(s => s.valueChanged);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
redirect(router: Router) {
|
|
78
|
+
assert.isTruthy(this.parameterSanitisers.length !== 0);
|
|
79
|
+
if (this.routeIdSanitiser.valueChanged) {
|
|
80
|
+
return router.navigate(['..', this.routeIdSanitiser.sanitisedValue]);
|
|
81
|
+
} else {
|
|
82
|
+
const routeParams = {};
|
|
83
|
+
this.parameterSanitisers.forEach(s => s.injectRouteParam(routeParams));
|
|
84
|
+
return router.navigate(['.', routeParams]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@Memoize()
|
|
89
|
+
private getSanitiser<T, S>(def: ParameterDefinition<T, S>): ParameterSanitiser<S> {
|
|
90
|
+
return def.sanitiser ? this.injector.get<ParameterSanitiser<S>>(def.sanitiser, new def.sanitiser()) : null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export namespace Sanitiser {
|
|
95
|
+
|
|
96
|
+
export class Value<T> extends Sanitiser<T, T> {
|
|
97
|
+
|
|
98
|
+
readFrom(snapshot: ActivatedRouteSnapshot): T {
|
|
99
|
+
const rawValue = this.parameterDef.isRouteId ?
|
|
100
|
+
snapshot.paramMap.get(this.parameterDef.name) :
|
|
101
|
+
snapshot.queryParamMap.get(this.parameterDef.name);
|
|
102
|
+
|
|
103
|
+
return this.parameterDef.type.parse(rawValue);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
protected hasChanged(initialValue: T, sanitisedValue: T): boolean {
|
|
107
|
+
return initialValue !== sanitisedValue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
injectRouteParam(dest: {}) {
|
|
111
|
+
dest[this.parameterDef.name] = this.parameterDef.type.format(this.sanitisedValue);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export class Array<T> extends Sanitiser<T, T[]> {
|
|
117
|
+
|
|
118
|
+
readFrom(snapshot: ActivatedRouteSnapshot): T[] {
|
|
119
|
+
const rawValues = snapshot.queryParamMap.getAll(this.parameterDef.name);
|
|
120
|
+
return rawValues.map(value => this.parameterDef.type.parse(value));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected hasChanged(initialValue: T[], sanitisedValue: T[]): boolean {
|
|
124
|
+
return initialValue !== sanitisedValue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
injectRouteParam(dest: {}) {
|
|
129
|
+
dest[this.parameterDef.name] = this.sanitisedValue.map(v => this.parameterDef.type.format(v));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import 'jest-preset-angular';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../dist/out-tsc",
|
|
5
|
+
"target": "es2015",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"inlineSources": true,
|
|
8
|
+
"types": [],
|
|
9
|
+
"lib": ["dom", "es2018"]
|
|
10
|
+
},
|
|
11
|
+
"angularCompilerOptions": {
|
|
12
|
+
"annotateForClosureCompiler": true,
|
|
13
|
+
"skipTemplateCodegen": true,
|
|
14
|
+
"strictMetadataEmit": true,
|
|
15
|
+
"fullTemplateTypeCheck": true,
|
|
16
|
+
"strictInjectionParameters": true,
|
|
17
|
+
"enableResourceInlining": true
|
|
18
|
+
},
|
|
19
|
+
"exclude": ["src/test.ts", "**/*.spec.ts"]
|
|
20
|
+
}
|