diff --git a/package.json b/package.json index 45698758..e9f8032e 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "karma-sourcemap-loader": "0.3.7", "merge2": "^1.0.2", "object.assign": "^4.0.4", - "require-dir": "^0.3.0", + "require-dir": "^0.3.2", "run-sequence": "^1.2.2", "through2": "^2.0.1", "typedoc": "^0.4.4", diff --git a/src/navigation-plan.js b/src/navigation-plan.js index 31f06ee0..f29ca19b 100644 --- a/src/navigation-plan.js +++ b/src/navigation-plan.js @@ -21,9 +21,7 @@ export class BuildNavigationPlanStep { } export function _buildNavigationPlan(instruction: NavigationInstruction, forceLifecycleMinimum): Promise { - let prev = instruction.previousInstruction; let config = instruction.config; - let plan = {}; if ('redirect' in config) { let redirectLocation = _resolveUrl(config.redirect, getInstructionBaseUrl(instruction)); @@ -34,66 +32,106 @@ export function _buildNavigationPlan(instruction: NavigationInstruction, forceLi return Promise.reject(new Redirect(redirectLocation)); } - if (prev) { - let newParams = hasDifferentParameterValues(prev, instruction); - let pending = []; + let prev = instruction.previousInstruction; + let plan = {}; + let pending = []; + let newParams = prev ? hasDifferentParameterValues(prev, instruction) : true; + + if (prev && !config.explicitViewPorts) { for (let viewPortName in prev.viewPortInstructions) { - let prevViewPortInstruction = prev.viewPortInstructions[viewPortName]; - let nextViewPortConfig = config.viewPorts[viewPortName]; - - if (!nextViewPortConfig) throw new Error(`Invalid Route Config: Configuration for viewPort "${viewPortName}" was not found for route: "${instruction.config.route}."`); - - let viewPortPlan = plan[viewPortName] = { - name: viewPortName, - config: nextViewPortConfig, - prevComponent: prevViewPortInstruction.component, - prevModuleId: prevViewPortInstruction.moduleId - }; - - if (prevViewPortInstruction.moduleId !== nextViewPortConfig.moduleId) { - viewPortPlan.strategy = activationStrategy.replace; - } else if ('determineActivationStrategy' in prevViewPortInstruction.component.viewModel) { - viewPortPlan.strategy = prevViewPortInstruction.component.viewModel - .determineActivationStrategy(...instruction.lifecycleArgs); - } else if (config.activationStrategy) { - viewPortPlan.strategy = config.activationStrategy; - } else if (newParams || forceLifecycleMinimum) { - viewPortPlan.strategy = activationStrategy.invokeLifecycle; - } else { - viewPortPlan.strategy = activationStrategy.noChange; + let viewPortPlan = buildViewPortPlan(instruction, forceLifecycleMinimum, newParams, viewPortName, true); + plan[viewPortPlan.name] = viewPortPlan.plan; + if (viewPortPlan.task) { + pending.push(viewPortPlan.task); } + } + } - if (viewPortPlan.strategy !== activationStrategy.replace && prevViewPortInstruction.childRouter) { - let path = instruction.getWildcardPath(); - let task = prevViewPortInstruction.childRouter - ._createNavigationInstruction(path, instruction).then(childInstruction => { // eslint-disable-line no-loop-func - viewPortPlan.childNavigationInstruction = childInstruction; - - return _buildNavigationPlan( - childInstruction, - viewPortPlan.strategy === activationStrategy.invokeLifecycle) - .then(childPlan => { - childInstruction.plan = childPlan; - }); - }); + let viewPortNames = {}; + if (config.viewPorts) { + for (let viewPortName in config.viewPorts) { + viewPortNames[viewPortName] = config.viewPorts[viewPortName]; + } + } - pending.push(task); - } + for (let viewPortName in viewPortNames) { + let viewPortPlan = buildViewPortPlan(instruction, forceLifecycleMinimum, newParams, viewPortName, false); + plan[viewPortPlan.name] = viewPortPlan.plan; + if (viewPortPlan.task) { + pending.push(viewPortPlan.task); } + } + + return Promise.all(pending).then(() => plan); +} - return Promise.all(pending).then(() => plan); +function buildViewPortPlan(instruction: NavigationInstruction, forceLifecycleMinimum, newParams: boolean, viewPortName: string, previous: boolean) { + let plan = {}; + let prev = instruction.previousInstruction; + let config = instruction.config; + let configViewPortName = viewPortName; + let prevViewPortInstruction = prev ? prev.viewPortInstructions[viewPortName] : undefined; + let nextViewPortConfig = !previous ? config.viewPorts[configViewPortName] : undefined; + + if (config.explicitViewPorts && nextViewPortConfig === undefined) { + nextViewPortConfig = null; + } + + plan['name'] = viewPortName; + let viewPortPlan = plan['plan'] = { + name: viewPortName + }; + if (prevViewPortInstruction) { + viewPortPlan.prevComponent = prevViewPortInstruction.component; + viewPortPlan.prevModuleId = prevViewPortInstruction.moduleId; + } + if (nextViewPortConfig) { + viewPortPlan.config = nextViewPortConfig; + viewPortPlan.active = true; } + else { + viewPortPlan.config = prevViewPortInstruction.config; + } + + if (!prevViewPortInstruction) { + viewPortPlan.strategy = activationStrategy.replace; + } else if (nextViewPortConfig === null) { // null value means deliberately cleared! + viewPortPlan.strategy = activationStrategy.replace; + } else if (nextViewPortConfig === undefined) { // undefined (left out in config) means keep same + viewPortPlan.strategy = activationStrategy.noChange; + } + else if (prevViewPortInstruction.moduleId !== nextViewPortConfig.moduleId) { + viewPortPlan.strategy = activationStrategy.replace; + } else if ('determineActivationStrategy' in prevViewPortInstruction.component.viewModel) { + viewPortPlan.strategy = prevViewPortInstruction.component.viewModel + .determineActivationStrategy(...instruction.lifecycleArgs); + } else if (config.activationStrategy) { + viewPortPlan.strategy = config.activationStrategy; + } else if (newParams || forceLifecycleMinimum) { + viewPortPlan.strategy = activationStrategy.invokeLifecycle; + } else { + viewPortPlan.strategy = activationStrategy.noChange; + } + + if (viewPortPlan.strategy !== activationStrategy.replace && prevViewPortInstruction.childRouter) { + let path = instruction.getWildcardPath(); + let task = prevViewPortInstruction.childRouter + ._createNavigationInstruction(path, instruction).then(childInstruction => { // eslint-disable-line no-loop-func + viewPortPlan.childNavigationInstruction = childInstruction; + + return _buildNavigationPlan( + childInstruction, + viewPortPlan.strategy === activationStrategy.invokeLifecycle) + .then(childPlan => { + childInstruction.plan = childPlan; + }); + }); - for (let viewPortName in config.viewPorts) { - plan[viewPortName] = { - name: viewPortName, - strategy: activationStrategy.replace, - config: instruction.config.viewPorts[viewPortName] - }; + plan['task'] = task; } - return Promise.resolve(plan); + return plan; } function hasDifferentParameterValues(prev: NavigationInstruction, next: NavigationInstruction): boolean { diff --git a/src/route-loading.js b/src/route-loading.js index a49d365c..2b19a89b 100644 --- a/src/route-loading.js +++ b/src/route-loading.js @@ -62,7 +62,7 @@ function determineWhatToLoad(navigationInstruction: NavigationInstruction, toLoa } function loadRoute(routeLoader: RouteLoader, navigationInstruction: NavigationInstruction, viewPortPlan: any) { - let moduleId = viewPortPlan.config.moduleId; + let moduleId = viewPortPlan.config ? viewPortPlan.config.moduleId : null; return loadComponent(routeLoader, navigationInstruction, viewPortPlan.config).then((component) => { let viewPortInstruction = navigationInstruction.addViewPortInstruction(