UNPKG

3.86 kBPlain TextView Raw
1import { Origin } from 'aurelia-metadata';
2import { relativeToFile } from 'aurelia-path';
3import { NavigationInstruction, RouteConfig, RouteLoader, Router } from 'aurelia-router';
4import { CompositionEngine, customElement, inlineView, useView, CompositionContext } from 'aurelia-templating';
5import { RouterViewLocator } from './router-view';
6import { Container } from 'aurelia-dependency-injection';
7
8/**@internal exported for unit testing */
9export class EmptyClass { }
10inlineView('<template></template>')(EmptyClass);
11
12/**
13 * Default implementation of `RouteLoader` used for loading component based on a route config
14 */
15export class TemplatingRouteLoader extends RouteLoader {
16
17 /**@internal */
18 static inject = [CompositionEngine];
19
20 /**@internal */
21 compositionEngine: CompositionEngine;
22
23 constructor(
24 compositionEngine: CompositionEngine
25 ) {
26 super();
27 this.compositionEngine = compositionEngine;
28 }
29
30 /**
31 * Resolve a view model from a RouteConfig
32 * Throws when there is neither "moduleId" nor "viewModel" property
33 * @internal
34 */
35 resolveViewModel(router: Router, config: RouteConfig): Promise<string | null | Function> {
36 return new Promise((resolve, reject) => {
37 let viewModel: string | null | Function;
38 if ('moduleId' in config) {
39 let moduleId = config.moduleId;
40 if (moduleId === null) {
41 viewModel = EmptyClass;
42 } else {
43 // this requires container of router has passes a certain point
44 // where a view model has been setup on the container
45 // it will fail in enhance scenario because no viewport has been registered
46 moduleId = relativeToFile(moduleId, Origin.get(router.container.viewModel.constructor).moduleId);
47 if (/\.html/i.test(moduleId)) {
48 viewModel = createDynamicClass(moduleId);
49 } else {
50 viewModel = moduleId;
51 }
52 }
53 return resolve(viewModel);
54 }
55 // todo: add if ('viewModel' in config) to support static view model resolution
56 reject(new Error('Invalid route config. No "moduleId" found.'));
57 });
58 }
59
60 /**
61 * Create child container based on a router container
62 * Also ensures that child router are properly constructed in the newly created child container
63 * @internal
64 */
65 createChildContainer(router: Router): Container {
66 const childContainer = router.container.createChild();
67
68 childContainer.registerSingleton(RouterViewLocator);
69 childContainer.getChildRouter = function() {
70 let childRouter: Router;
71
72 childContainer.registerHandler(
73 Router,
74 () => childRouter || (childRouter = router.createChild(childContainer))
75 );
76
77 return childContainer.get(Router);
78 };
79 return childContainer;
80 }
81
82 /**
83 * Load corresponding component of a route config of a navigation instruction
84 */
85 // eslint-disable-next-line @typescript-eslint/no-unused-vars
86 loadRoute(router: Router, config: RouteConfig, navInstruction: NavigationInstruction): Promise<any> {
87 return this
88 .resolveViewModel(router, config)
89 .then(viewModel => this.compositionEngine.ensureViewModel({
90 viewModel: viewModel,
91 childContainer: this.createChildContainer(router),
92 view: config.view || config.viewStrategy,
93 router: router
94 } as CompositionContext));
95 }
96}
97
98/**@internal exported for unit testing */
99export function createDynamicClass(moduleId: string) {
100 const name = /([^\/^\?]+)\.html/i.exec(moduleId)[1];
101
102 class DynamicClass {
103
104 $parent: any;
105
106 bind(bindingContext: any) {
107 this.$parent = bindingContext;
108 }
109 }
110
111 customElement(name)(DynamicClass);
112 useView(moduleId)(DynamicClass);
113
114 return DynamicClass;
115}