2015-11-06 17:34:07 -08:00
|
|
|
import {Promise, PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
|
|
|
import {Map, StringMapWrapper, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
|
|
|
import {isBlank, isString, isPresent, Type, isArray} from 'angular2/src/facade/lang';
|
|
|
|
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
import {Inject, Injectable} from 'angular2/core';
|
|
|
|
|
|
|
|
import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './route_registry';
|
2015-09-23 00:10:26 -07:00
|
|
|
import {
|
|
|
|
ComponentInstruction,
|
|
|
|
Instruction,
|
|
|
|
} from './instruction';
|
2015-04-17 09:59:56 -07:00
|
|
|
import {RouterOutlet} from './router_outlet';
|
2015-04-21 11:23:23 -07:00
|
|
|
import {Location} from './location';
|
2015-07-07 15:44:29 -07:00
|
|
|
import {getCanActivateHook} from './route_lifecycle_reflector';
|
2015-07-13 16:12:48 -07:00
|
|
|
import {RouteDefinition} from './route_config_impl';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-06-29 10:40:16 +02:00
|
|
|
let _resolveToTrue = PromiseWrapper.resolve(true);
|
|
|
|
let _resolveToFalse = PromiseWrapper.resolve(false);
|
|
|
|
|
2015-04-17 09:59:56 -07:00
|
|
|
/**
|
2015-09-03 16:17:23 -07:00
|
|
|
* The `Router` is responsible for mapping URLs to components.
|
2015-04-17 09:59:56 -07:00
|
|
|
*
|
|
|
|
* You can see the state of the router by inspecting the read-only field `router.navigating`.
|
|
|
|
* This may be useful for showing a spinner, for instance.
|
|
|
|
*
|
2015-05-14 13:01:48 -07:00
|
|
|
* ## Concepts
|
2015-09-03 16:17:23 -07:00
|
|
|
*
|
2015-05-14 13:01:48 -07:00
|
|
|
* Routers and component instances have a 1:1 correspondence.
|
|
|
|
*
|
2015-09-03 16:17:23 -07:00
|
|
|
* The router holds reference to a number of {@link RouterOutlet}.
|
|
|
|
* An outlet is a placeholder that the router dynamically fills in depending on the current URL.
|
2015-05-14 13:01:48 -07:00
|
|
|
*
|
2015-09-11 21:49:41 -07:00
|
|
|
* When the router navigates from a URL, it must first recognize it and serialize it into an
|
2015-05-29 14:58:41 -07:00
|
|
|
* `Instruction`.
|
2015-05-14 13:01:48 -07:00
|
|
|
* The router uses the `RouteRegistry` to get an `Instruction`.
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
|
|
|
export class Router {
|
2015-06-29 10:37:55 +02:00
|
|
|
navigating: boolean = false;
|
2015-04-17 09:59:56 -07:00
|
|
|
lastNavigationAttempt: string;
|
2015-06-29 10:37:55 +02:00
|
|
|
|
|
|
|
private _currentInstruction: Instruction = null;
|
2015-08-30 21:25:46 -07:00
|
|
|
|
2015-06-29 10:40:16 +02:00
|
|
|
private _currentNavigation: Promise<any> = _resolveToTrue;
|
2015-06-29 10:37:55 +02:00
|
|
|
private _outlet: RouterOutlet = null;
|
2015-08-30 21:25:46 -07:00
|
|
|
|
2015-09-29 11:11:06 -07:00
|
|
|
private _auxRouters = new Map<string, Router>();
|
2015-08-30 21:25:46 -07:00
|
|
|
private _childRouter: Router;
|
|
|
|
|
2015-10-24 18:48:43 -07:00
|
|
|
private _subject: EventEmitter<any> = new EventEmitter();
|
2015-05-29 14:58:41 -07:00
|
|
|
|
2015-08-30 21:25:46 -07:00
|
|
|
|
2015-08-24 14:39:54 -07:00
|
|
|
constructor(public registry: RouteRegistry, public parent: Router, public hostComponent: any) {}
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-29 14:58:41 -07:00
|
|
|
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
|
|
|
|
* component.
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
childRouter(hostComponent: any): Router {
|
|
|
|
return this._childRouter = new ChildRouter(this, hostComponent);
|
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-30 21:25:46 -07:00
|
|
|
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
|
|
|
|
* component.
|
|
|
|
*/
|
|
|
|
auxRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register an outlet to notified of primary route changes.
|
|
|
|
*
|
|
|
|
* You probably don't need to use this unless you're writing a reusable component.
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
registerPrimaryOutlet(outlet: RouterOutlet): Promise<boolean> {
|
2015-07-17 13:36:53 -07:00
|
|
|
if (isPresent(outlet.name)) {
|
2015-10-26 13:58:00 +00:00
|
|
|
throw new BaseException(`registerPrimaryOutlet expects to be called with an unnamed outlet.`);
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
2015-08-30 21:25:46 -07:00
|
|
|
|
|
|
|
this._outlet = outlet;
|
2015-05-07 21:10:12 -07:00
|
|
|
if (isPresent(this._currentInstruction)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
return this.commit(this._currentInstruction, false);
|
|
|
|
}
|
|
|
|
return _resolveToTrue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register an outlet to notified of auxiliary route changes.
|
|
|
|
*
|
|
|
|
* You probably don't need to use this unless you're writing a reusable component.
|
|
|
|
*/
|
|
|
|
registerAuxOutlet(outlet: RouterOutlet): Promise<boolean> {
|
|
|
|
var outletName = outlet.name;
|
|
|
|
if (isBlank(outletName)) {
|
|
|
|
throw new BaseException(`registerAuxOutlet expects to be called with an outlet with a name.`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO...
|
|
|
|
// what is the host of an aux route???
|
|
|
|
var router = this.auxRouter(this.hostComponent);
|
|
|
|
|
|
|
|
this._auxRouters.set(outletName, router);
|
|
|
|
router._outlet = outlet;
|
|
|
|
|
|
|
|
var auxInstruction;
|
|
|
|
if (isPresent(this._currentInstruction) &&
|
|
|
|
isPresent(auxInstruction = this._currentInstruction.auxInstruction[outletName])) {
|
|
|
|
return router.commit(auxInstruction);
|
2015-05-07 21:10:12 -07:00
|
|
|
}
|
2015-06-29 10:40:16 +02:00
|
|
|
return _resolveToTrue;
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-30 21:25:46 -07:00
|
|
|
/**
|
|
|
|
* Given an instruction, returns `true` if the instruction is currently active,
|
|
|
|
* otherwise `false`.
|
|
|
|
*/
|
|
|
|
isRouteActive(instruction: Instruction): boolean {
|
|
|
|
var router = this;
|
|
|
|
while (isPresent(router.parent) && isPresent(instruction.child)) {
|
|
|
|
router = router.parent;
|
|
|
|
instruction = instruction.child;
|
|
|
|
}
|
|
|
|
return isPresent(this._currentInstruction) &&
|
|
|
|
this._currentInstruction.component == instruction.component;
|
|
|
|
}
|
|
|
|
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
|
2015-04-17 09:59:56 -07:00
|
|
|
/**
|
2015-05-14 13:01:48 -07:00
|
|
|
* Dynamically update the routing configuration and trigger a navigation.
|
2015-04-17 09:59:56 -07:00
|
|
|
*
|
2015-11-17 09:41:31 -08:00
|
|
|
* ### Usage
|
2015-04-17 09:59:56 -07:00
|
|
|
*
|
|
|
|
* ```
|
2015-04-29 15:47:12 -07:00
|
|
|
* router.config([
|
|
|
|
* { 'path': '/', 'component': IndexComp },
|
|
|
|
* { 'path': '/user/:id', 'component': UserComp },
|
|
|
|
* ]);
|
|
|
|
* ```
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
2015-08-28 11:29:19 -07:00
|
|
|
config(definitions: RouteDefinition[]): Promise<any> {
|
2015-07-17 13:36:53 -07:00
|
|
|
definitions.forEach(
|
|
|
|
(routeDefinition) => { this.registry.config(this.hostComponent, routeDefinition); });
|
2015-04-17 09:59:56 -07:00
|
|
|
return this.renavigate();
|
|
|
|
}
|
|
|
|
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
|
2015-09-08 21:42:23 -07:00
|
|
|
/**
|
|
|
|
* Navigate based on the provided Route Link DSL. It's preferred to navigate with this method
|
|
|
|
* over `navigateByUrl`.
|
|
|
|
*
|
2015-11-17 09:41:31 -08:00
|
|
|
* ### Usage
|
2015-09-08 21:42:23 -07:00
|
|
|
*
|
|
|
|
* This method takes an array representing the Route Link DSL:
|
|
|
|
* ```
|
|
|
|
* ['./MyCmp', {param: 3}]
|
|
|
|
* ```
|
|
|
|
* See the {@link RouterLink} directive for more.
|
|
|
|
*/
|
|
|
|
navigate(linkParams: any[]): Promise<any> {
|
|
|
|
var instruction = this.generate(linkParams);
|
|
|
|
return this.navigateByInstruction(instruction, false);
|
|
|
|
}
|
|
|
|
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
/**
|
2015-05-21 13:59:14 -07:00
|
|
|
* Navigate to a URL. Returns a promise that resolves when navigation is complete.
|
2015-09-08 21:42:23 -07:00
|
|
|
* It's preferred to navigate with `navigate` instead of this method, since URLs are more brittle.
|
2015-05-14 13:01:48 -07:00
|
|
|
*
|
|
|
|
* If the given URL begins with a `/`, router will navigate absolutely.
|
|
|
|
* If the given URL does not begin with `/`, the router will navigate relative to this component.
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
2015-09-08 21:41:56 -07:00
|
|
|
navigateByUrl(url: string, _skipLocationChange: boolean = false): Promise<any> {
|
2015-07-07 15:44:29 -07:00
|
|
|
return this._currentNavigation = this._currentNavigation.then((_) => {
|
|
|
|
this.lastNavigationAttempt = url;
|
2015-05-21 13:59:14 -07:00
|
|
|
this._startNavigating();
|
2015-07-17 13:36:53 -07:00
|
|
|
return this._afterPromiseFinishNavigating(this.recognize(url).then((instruction) => {
|
|
|
|
if (isBlank(instruction)) {
|
2015-07-07 15:44:29 -07:00
|
|
|
return false;
|
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
return this._navigate(instruction, _skipLocationChange);
|
2015-07-07 15:44:29 -07:00
|
|
|
}));
|
2015-05-21 13:59:14 -07:00
|
|
|
});
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
2015-07-17 13:36:53 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Navigate via the provided instruction. Returns a promise that resolves when navigation is
|
|
|
|
* complete.
|
|
|
|
*/
|
2015-09-08 21:41:56 -07:00
|
|
|
navigateByInstruction(instruction: Instruction,
|
|
|
|
_skipLocationChange: boolean = false): Promise<any> {
|
2015-07-17 13:36:53 -07:00
|
|
|
if (isBlank(instruction)) {
|
|
|
|
return _resolveToFalse;
|
|
|
|
}
|
|
|
|
return this._currentNavigation = this._currentNavigation.then((_) => {
|
|
|
|
this._startNavigating();
|
|
|
|
return this._afterPromiseFinishNavigating(this._navigate(instruction, _skipLocationChange));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-10-09 17:21:25 -07:00
|
|
|
/** @internal */
|
2015-07-17 13:36:53 -07:00
|
|
|
_navigate(instruction: Instruction, _skipLocationChange: boolean): Promise<any> {
|
2015-08-14 12:31:56 -07:00
|
|
|
return this._settleInstruction(instruction)
|
2015-08-30 21:25:46 -07:00
|
|
|
.then((_) => this._canReuse(instruction))
|
2015-07-17 13:36:53 -07:00
|
|
|
.then((_) => this._canActivate(instruction))
|
|
|
|
.then((result) => {
|
|
|
|
if (!result) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return this._canDeactivate(instruction)
|
|
|
|
.then((result) => {
|
|
|
|
if (result) {
|
|
|
|
return this.commit(instruction, _skipLocationChange)
|
|
|
|
.then((_) => {
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
this._emitNavigationFinish(instruction.toRootUrl());
|
2015-07-17 13:36:53 -07:00
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-10-09 17:21:25 -07:00
|
|
|
/** @internal */
|
2015-08-14 12:31:56 -07:00
|
|
|
_settleInstruction(instruction: Instruction): Promise<any> {
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
return instruction.resolveComponent().then((_) => {
|
|
|
|
instruction.component.reuse = false;
|
|
|
|
|
|
|
|
var unsettledInstructions: Array<Promise<any>> = [];
|
|
|
|
|
|
|
|
if (isPresent(instruction.child)) {
|
|
|
|
unsettledInstructions.push(this._settleInstruction(instruction.child));
|
|
|
|
}
|
|
|
|
|
|
|
|
StringMapWrapper.forEach(instruction.auxInstruction, (instruction, _) => {
|
|
|
|
unsettledInstructions.push(this._settleInstruction(instruction));
|
|
|
|
});
|
|
|
|
return PromiseWrapper.all(unsettledInstructions);
|
2015-08-14 12:31:56 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-11-15 23:58:59 -08:00
|
|
|
private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); }
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-07-07 15:44:29 -07:00
|
|
|
private _afterPromiseFinishNavigating(promise: Promise<any>): Promise<any> {
|
|
|
|
return PromiseWrapper.catchError(promise.then((_) => this._finishNavigating()), (err) => {
|
|
|
|
this._finishNavigating();
|
|
|
|
throw err;
|
|
|
|
});
|
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-08-30 21:25:46 -07:00
|
|
|
/*
|
|
|
|
* Recursively set reuse flags
|
|
|
|
*/
|
2015-10-09 17:21:25 -07:00
|
|
|
/** @internal */
|
2015-08-30 21:25:46 -07:00
|
|
|
_canReuse(instruction: Instruction): Promise<any> {
|
2015-07-07 15:44:29 -07:00
|
|
|
if (isBlank(this._outlet)) {
|
|
|
|
return _resolveToFalse;
|
|
|
|
}
|
2015-08-30 21:25:46 -07:00
|
|
|
return this._outlet.canReuse(instruction.component)
|
2015-07-07 15:44:29 -07:00
|
|
|
.then((result) => {
|
2015-08-30 20:37:23 -07:00
|
|
|
instruction.component.reuse = result;
|
2015-09-09 12:00:31 -07:00
|
|
|
if (result && isPresent(this._childRouter) && isPresent(instruction.child)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
return this._childRouter._canReuse(instruction.child);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-05-14 13:01:48 -07:00
|
|
|
|
2015-07-17 13:36:53 -07:00
|
|
|
private _canActivate(nextInstruction: Instruction): Promise<boolean> {
|
|
|
|
return canActivateOne(nextInstruction, this._currentInstruction);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-07-07 15:44:29 -07:00
|
|
|
private _canDeactivate(instruction: Instruction): Promise<boolean> {
|
|
|
|
if (isBlank(this._outlet)) {
|
|
|
|
return _resolveToTrue;
|
|
|
|
}
|
|
|
|
var next: Promise<boolean>;
|
2015-08-30 21:25:46 -07:00
|
|
|
var childInstruction: Instruction = null;
|
|
|
|
var reuse: boolean = false;
|
|
|
|
var componentInstruction: ComponentInstruction = null;
|
|
|
|
if (isPresent(instruction)) {
|
|
|
|
childInstruction = instruction.child;
|
|
|
|
componentInstruction = instruction.component;
|
|
|
|
reuse = instruction.component.reuse;
|
|
|
|
}
|
|
|
|
if (reuse) {
|
2015-07-07 15:44:29 -07:00
|
|
|
next = _resolveToTrue;
|
|
|
|
} else {
|
2015-08-30 21:25:46 -07:00
|
|
|
next = this._outlet.canDeactivate(componentInstruction);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
// TODO: aux route lifecycle hooks
|
2015-07-07 15:44:29 -07:00
|
|
|
return next.then((result) => {
|
|
|
|
if (result == false) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-08-30 21:25:46 -07:00
|
|
|
if (isPresent(this._childRouter)) {
|
|
|
|
return this._childRouter._canDeactivate(childInstruction);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-05-14 13:01:48 -07:00
|
|
|
/**
|
2015-05-21 13:59:14 -07:00
|
|
|
* Updates this router and all descendant routers according to the given instruction
|
2015-05-14 13:01:48 -07:00
|
|
|
*/
|
2015-07-28 01:46:09 -04:00
|
|
|
commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise<any> {
|
2015-05-14 13:01:48 -07:00
|
|
|
this._currentInstruction = instruction;
|
2015-08-30 21:25:46 -07:00
|
|
|
var next: Promise<any> = _resolveToTrue;
|
2015-05-21 13:59:14 -07:00
|
|
|
if (isPresent(this._outlet)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
var componentInstruction = instruction.component;
|
|
|
|
if (componentInstruction.reuse) {
|
|
|
|
next = this._outlet.reuse(componentInstruction);
|
|
|
|
} else {
|
|
|
|
next =
|
|
|
|
this.deactivate(instruction).then((_) => this._outlet.activate(componentInstruction));
|
|
|
|
}
|
|
|
|
if (isPresent(instruction.child)) {
|
|
|
|
next = next.then((_) => {
|
|
|
|
if (isPresent(this._childRouter)) {
|
|
|
|
return this._childRouter.commit(instruction.child);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-05-21 13:59:14 -07:00
|
|
|
}
|
2015-08-30 21:25:46 -07:00
|
|
|
|
2015-07-17 13:36:53 -07:00
|
|
|
var promises = [];
|
2015-10-30 17:05:30 -07:00
|
|
|
this._auxRouters.forEach((router, name) => {
|
|
|
|
if (isPresent(instruction.auxInstruction[name])) {
|
|
|
|
promises.push(router.commit(instruction.auxInstruction[name]));
|
|
|
|
}
|
|
|
|
});
|
2015-08-30 21:25:46 -07:00
|
|
|
|
2015-07-17 13:36:53 -07:00
|
|
|
return next.then((_) => PromiseWrapper.all(promises));
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
2015-05-14 13:01:48 -07:00
|
|
|
|
2015-10-09 17:21:25 -07:00
|
|
|
/** @internal */
|
2015-07-07 15:44:29 -07:00
|
|
|
_startNavigating(): void { this.navigating = true; }
|
|
|
|
|
2015-10-09 17:21:25 -07:00
|
|
|
/** @internal */
|
2015-07-07 15:44:29 -07:00
|
|
|
_finishNavigating(): void { this.navigating = false; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Subscribe to URL updates from the router
|
|
|
|
*/
|
2015-08-18 11:30:32 -07:00
|
|
|
subscribe(onNext: (value: any) => void): Object {
|
|
|
|
return ObservableWrapper.subscribe(this._subject, onNext);
|
2015-07-07 20:03:00 -07:00
|
|
|
}
|
2015-07-07 15:44:29 -07:00
|
|
|
|
|
|
|
|
2015-05-14 13:01:48 -07:00
|
|
|
/**
|
2015-05-21 13:59:14 -07:00
|
|
|
* Removes the contents of this router's outlet and all descendant outlets
|
2015-05-14 13:01:48 -07:00
|
|
|
*/
|
2015-07-07 15:44:29 -07:00
|
|
|
deactivate(instruction: Instruction): Promise<any> {
|
2015-08-30 21:25:46 -07:00
|
|
|
var childInstruction: Instruction = null;
|
|
|
|
var componentInstruction: ComponentInstruction = null;
|
|
|
|
if (isPresent(instruction)) {
|
|
|
|
childInstruction = instruction.child;
|
|
|
|
componentInstruction = instruction.component;
|
|
|
|
}
|
|
|
|
var next: Promise<any> = _resolveToTrue;
|
|
|
|
if (isPresent(this._childRouter)) {
|
|
|
|
next = this._childRouter.deactivate(childInstruction);
|
|
|
|
}
|
2015-05-21 13:59:14 -07:00
|
|
|
if (isPresent(this._outlet)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
next = next.then((_) => this._outlet.deactivate(componentInstruction));
|
2015-05-21 13:59:14 -07:00
|
|
|
}
|
2015-08-30 21:25:46 -07:00
|
|
|
|
|
|
|
// TODO: handle aux routes
|
|
|
|
|
|
|
|
return next;
|
2015-05-14 13:01:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-17 09:59:56 -07:00
|
|
|
/**
|
|
|
|
* Given a URL, returns an instruction representing the component graph
|
|
|
|
*/
|
2015-05-21 13:59:14 -07:00
|
|
|
recognize(url: string): Promise<Instruction> {
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
var ancestorComponents = this._getAncestorInstructions();
|
|
|
|
return this.registry.recognize(url, ancestorComponents);
|
|
|
|
}
|
|
|
|
|
|
|
|
private _getAncestorInstructions(): Instruction[] {
|
|
|
|
var ancestorComponents = [];
|
|
|
|
var ancestorRouter = this;
|
|
|
|
while (isPresent(ancestorRouter.parent) &&
|
|
|
|
isPresent(ancestorRouter.parent._currentInstruction)) {
|
|
|
|
ancestorRouter = ancestorRouter.parent;
|
|
|
|
ancestorComponents.unshift(ancestorRouter._currentInstruction);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ancestorComponents;
|
2015-05-21 13:59:14 -07:00
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-29 14:58:41 -07:00
|
|
|
* Navigates to either the last URL successfully navigated to, or the last URL requested if the
|
|
|
|
* router has yet to successfully navigate.
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
2015-05-29 14:58:41 -07:00
|
|
|
renavigate(): Promise<any> {
|
2015-07-07 15:44:29 -07:00
|
|
|
if (isBlank(this.lastNavigationAttempt)) {
|
2015-05-21 13:59:14 -07:00
|
|
|
return this._currentNavigation;
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
2015-09-08 21:41:56 -07:00
|
|
|
return this.navigateByUrl(this.lastNavigationAttempt);
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-05-29 14:58:41 -07:00
|
|
|
* Generate a URL from a component name and optional map of parameters. The URL is relative to the
|
|
|
|
* app's base href.
|
2015-04-17 09:59:56 -07:00
|
|
|
*/
|
2015-08-28 11:29:19 -07:00
|
|
|
generate(linkParams: any[]): Instruction {
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
var ancestorInstructions = this._getAncestorInstructions();
|
|
|
|
return this.registry.generate(linkParams, ancestorInstructions);
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
@Injectable()
|
2015-04-17 09:59:56 -07:00
|
|
|
export class RootRouter extends Router {
|
2015-10-09 17:21:25 -07:00
|
|
|
/** @internal */
|
2015-05-29 14:58:41 -07:00
|
|
|
_location: Location;
|
2015-10-28 14:16:54 -07:00
|
|
|
/** @internal */
|
2015-10-26 11:01:02 -07:00
|
|
|
_locationSub: Object;
|
2015-05-14 13:01:48 -07:00
|
|
|
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
constructor(registry: RouteRegistry, location: Location,
|
|
|
|
@Inject(ROUTER_PRIMARY_COMPONENT) primaryComponent: Type) {
|
2015-09-03 12:55:08 -07:00
|
|
|
super(registry, null, primaryComponent);
|
2015-05-14 13:01:48 -07:00
|
|
|
this._location = location;
|
2015-10-26 11:01:02 -07:00
|
|
|
this._locationSub = this._location.subscribe(
|
|
|
|
(change) => this.navigateByUrl(change['url'], isPresent(change['pop'])));
|
2015-09-03 12:55:08 -07:00
|
|
|
this.registry.configFromComponent(primaryComponent);
|
2015-09-08 21:41:56 -07:00
|
|
|
this.navigateByUrl(location.path());
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
2015-05-14 13:01:48 -07:00
|
|
|
|
2015-07-28 01:46:09 -04:00
|
|
|
commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise<any> {
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
var emitPath = instruction.toUrlPath();
|
|
|
|
var emitQuery = instruction.toUrlQuery();
|
2015-09-23 00:10:26 -07:00
|
|
|
if (emitPath.length > 0) {
|
|
|
|
emitPath = '/' + emitPath;
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
2015-07-28 01:46:09 -04:00
|
|
|
var promise = super.commit(instruction);
|
|
|
|
if (!_skipLocationChange) {
|
2015-09-23 00:10:26 -07:00
|
|
|
promise = promise.then((_) => { this._location.go(emitPath, emitQuery); });
|
2015-07-28 01:46:09 -04:00
|
|
|
}
|
|
|
|
return promise;
|
2015-05-14 13:01:48 -07:00
|
|
|
}
|
2015-10-26 11:01:02 -07:00
|
|
|
|
|
|
|
dispose(): void {
|
|
|
|
if (isPresent(this._locationSub)) {
|
|
|
|
ObservableWrapper.dispose(this._locationSub);
|
|
|
|
this._locationSub = null;
|
|
|
|
}
|
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
class ChildRouter extends Router {
|
2015-05-29 14:58:41 -07:00
|
|
|
constructor(parent: Router, hostComponent) {
|
2015-08-24 14:39:54 -07:00
|
|
|
super(parent.registry, parent, hostComponent);
|
2015-04-17 09:59:56 -07:00
|
|
|
this.parent = parent;
|
|
|
|
}
|
2015-07-01 16:37:36 -07:00
|
|
|
|
|
|
|
|
2015-09-08 21:41:56 -07:00
|
|
|
navigateByUrl(url: string, _skipLocationChange: boolean = false): Promise<any> {
|
2015-07-01 16:37:36 -07:00
|
|
|
// Delegate navigation to the root router
|
2015-09-08 21:41:56 -07:00
|
|
|
return this.parent.navigateByUrl(url, _skipLocationChange);
|
2015-07-01 16:37:36 -07:00
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-09-08 21:41:56 -07:00
|
|
|
navigateByInstruction(instruction: Instruction,
|
|
|
|
_skipLocationChange: boolean = false): Promise<any> {
|
2015-07-17 13:36:53 -07:00
|
|
|
// Delegate navigation to the root router
|
2015-09-08 21:41:56 -07:00
|
|
|
return this.parent.navigateByInstruction(instruction, _skipLocationChange);
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
2015-07-06 17:41:15 -07:00
|
|
|
|
2015-07-07 15:44:29 -07:00
|
|
|
|
2015-10-28 08:59:19 +01:00
|
|
|
function canActivateOne(nextInstruction: Instruction,
|
|
|
|
prevInstruction: Instruction): Promise<boolean> {
|
2015-07-07 15:44:29 -07:00
|
|
|
var next = _resolveToTrue;
|
|
|
|
if (isPresent(nextInstruction.child)) {
|
|
|
|
next = canActivateOne(nextInstruction.child,
|
2015-07-17 13:36:53 -07:00
|
|
|
isPresent(prevInstruction) ? prevInstruction.child : null);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
return next.then((result) => {
|
|
|
|
if (result == false) {
|
2015-07-07 15:44:29 -07:00
|
|
|
return false;
|
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
if (nextInstruction.component.reuse) {
|
2015-07-07 15:44:29 -07:00
|
|
|
return true;
|
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
var hook = getCanActivateHook(nextInstruction.component.componentType);
|
2015-07-07 15:44:29 -07:00
|
|
|
if (isPresent(hook)) {
|
2015-07-17 13:36:53 -07:00
|
|
|
return hook(nextInstruction.component,
|
|
|
|
isPresent(prevInstruction) ? prevInstruction.component : null);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|