Skip to content
This repository has been archived by the owner on Jul 30, 2018. It is now read-only.

Multiple outlets #110

Merged
merged 8 commits into from
Nov 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ const FooOutlet = Outlet(MyViewWidget, 'foo', (options: MapParamsOptions) {
});
```


When there are multiple matching outlets, the callback function receives all matching parameters merged into a single object.

##### Global Error Outlet

Whenever a `MatchType.ERROR` occurs a global outlet is automatically added to the matched outlets called `errorOutlet`. This outlet can be used to render a widget for any unknown routes.
Expand Down Expand Up @@ -228,6 +231,8 @@ router.link('foo')

A default route can be specified using the optional configuration property `defaultRoute`, which will be used if the current route does not match a registered route. Note there can only be one default route configured otherwise an error will be thrown.

In the case that multiple outlets match, for example where a nested path has an exact match, and a parent path has a partial match, the deepest registered outlet is returned.

#### Registering Additional Routes

Additional routing configuration can be registered with a router instance, either from the root or by specifying an existing outlet name.
Expand Down
2 changes: 1 addition & 1 deletion src/Outlet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type Outlet<

export function Outlet<W extends WidgetBaseInterface, F extends WidgetBaseInterface, E extends WidgetBaseInterface>(
outletComponents: Component<W> | OutletComponents<W, F, E>,
outlet: string,
outlet: string | string[],
mapParams: MapParams = (options: MapParamsOptions) => {},
key: RegistryLabel = routerKey
): Outlet<W, F, E> {
Expand Down
26 changes: 24 additions & 2 deletions src/Router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,30 @@ export class Router<C extends Context> extends Evented implements RouterInterfac
return this._outletContextMap.has(outletId);
}

getOutlet(outletId: string): OutletContext | undefined {
return this._outletContextMap.get(outletId);
getOutlet(outletId: string | string[]): OutletContext | undefined {
const outletIds = Array.isArray(outletId) ? outletId : [ outletId ];
let matchingOutlet: OutletContext | undefined = undefined;
let matchingParams: Parameters = {};
let matchingLocation = '';

for (let i = 0; i < outletIds.length; i++) {
const outletContext = this._outletContextMap.get(outletIds[i]);

if (outletContext) {
const { params, location } = outletContext;
matchingParams = { ...matchingParams, ...params };

if (!matchingOutlet || matchingLocation.indexOf(location) === -1) {
matchingLocation = location;
matchingOutlet = {
...outletContext,
params: matchingParams
};
}
}
}

return matchingOutlet;
}

getCurrentParams(): Parameters {
Expand Down
103 changes: 102 additions & 1 deletion tests/unit/Router.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Task from '@dojo/core/async/Task';
import Promise from '@dojo/shim/Promise';
const { beforeEach, suite, test } = intern.getInterface('tdd');
const { assert } = intern.getPlugin('chai');
import { spy, stub } from 'sinon';
import MemoryHistory from '../../src/history/MemoryHistory';

import {
Context,
DefaultParameters,
Expand Down Expand Up @@ -503,6 +503,107 @@ suite('Router', () => {
});
});

test('query for multiple outlets', async () => {
const config = [{
path: '/path-1',
outlet: 'outlet-id-1'
}, {
path: '/path-2',
outlet: 'outlet-id-2',
children: [{
path: '/nested-path',
outlet: 'outlet-id-3',
children: [{
path: '/nested-path',
outlet: 'outlet-id-4',
children: [{
path: '/nested-path',
outlet: 'outlet-id-5'
}]
}]
}]
}, {
path: '/path-3',
outlet: 'outlet-id-5'
}];

const router = new Router({ config });

await router.dispatch({}, '/path-2');

const noMatchResult = router.getOutlet(['no', 'outlet-id-1', '', ' ']);

assert.equal(noMatchResult, undefined);

const matchingResult = router.getOutlet(['true', 'outlet-id-2']);

assert.deepEqual(matchingResult, {
location: '/path-2',
type: MatchType.INDEX,
params: {}
});

const emptyInput = router.getOutlet([]);
assert.equal(emptyInput, undefined);

await router.dispatch({}, '/path-2/nested-path');

const multipleMatchingOutlets = router.getOutlet(['outlet-id-2', 'outlet-id-3']);

assert.deepEqual(multipleMatchingOutlets, {
location: '/path-2/nested-path',
type: MatchType.INDEX,
params: {}
});

await router.dispatch({}, '/path-2/nested-path/nested-path');

assert.deepEqual(router.getOutlet(['outlet-id-4']), {
location: '/path-2/nested-path/nested-path',
type: MatchType.INDEX,
params: {}
});

assert.deepEqual(router.getOutlet(['outlet-id-4', 'outlet-id-2']), {
location: '/path-2/nested-path/nested-path',
type: MatchType.INDEX,
params: {}
});
});

test('parameters are combined with multiple matching outlets', async () => {
const config = [{
path: '/path',
outlet: 'outlet-id-1',
children: [{
path: '/nested-path/{outlet-2-param}',
outlet: 'outlet-id-2',
children: [{
path: '/nested-path/{outlet-3-param}',
outlet: 'outlet-id-3',
children: [{
path: '/nested-path/{outlet-4-param}',
outlet: 'outlet-id-4'
}]
}]
}]
}];

const router = new Router({ config });

await router.dispatch({}, '/path/nested-path/param-2/nested-path/param-3/nested-path/param-4');

assert.deepEqual(router.getOutlet(['outlet-id-3', 'outlet-id-2', 'outlet-id-4']), {
location: '/path/nested-path/param-2/nested-path/param-3/nested-path/param-4',
type: MatchType.INDEX,
params: {
'outlet-2-param': 'param-2',
'outlet-3-param': 'param-3',
'outlet-4-param': 'param-4'
}
});
});

test('register() throws error if more than one default route is attempted to be registered', () => {
const config = [
{
Expand Down