Skip to content

Commit

Permalink
feat(router): optional viewports
Browse files Browse the repository at this point in the history
Makes viewports optional in route configuration. Enables configuring viewports to be either empty or contain previous module.

Depending on aurelia/templating-router/optional-viewports.
Partially closes /issues/482.
  • Loading branch information
jwx committed Oct 15, 2017
1 parent dffe907 commit 8878b45
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 53 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
140 changes: 89 additions & 51 deletions src/navigation-plan.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ export class BuildNavigationPlanStep {
}

export function _buildNavigationPlan(instruction: NavigationInstruction, forceLifecycleMinimum): Promise<Object> {
let prev = instruction.previousInstruction;
let config = instruction.config;
let plan = {};

if ('redirect' in config) {
let redirectLocation = _resolveUrl(config.redirect, getInstructionBaseUrl(instruction));
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/route-loading.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 8878b45

Please sign in to comment.