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 21:07:37 -05:00
|
|
|
import {
|
2015-12-02 14:54:10 -05:00
|
|
|
ComponentFixture,
|
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 21:07:37 -05:00
|
|
|
AsyncTestCompleter,
|
|
|
|
TestComponentBuilder,
|
|
|
|
beforeEach,
|
|
|
|
ddescribe,
|
|
|
|
xdescribe,
|
|
|
|
describe,
|
|
|
|
el,
|
|
|
|
expect,
|
|
|
|
iit,
|
|
|
|
inject,
|
|
|
|
beforeEachProviders,
|
|
|
|
it,
|
|
|
|
xit
|
|
|
|
} from 'angular2/testing_internal';
|
|
|
|
|
|
|
|
import {provide, Component, Injector, Inject} from 'angular2/core';
|
|
|
|
|
|
|
|
import {Router, ROUTER_DIRECTIVES, RouteParams, RouteData, Location} from 'angular2/router';
|
|
|
|
import {RouteConfig, Route, AuxRoute, Redirect} from 'angular2/src/router/route_config_decorator';
|
|
|
|
|
|
|
|
import {TEST_ROUTER_PROVIDERS, RootCmp, compile, clickOnElement, getHref} from './util';
|
|
|
|
|
2015-12-02 14:54:10 -05:00
|
|
|
function getLinkElement(rtc: ComponentFixture) {
|
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 21:07:37 -05:00
|
|
|
return rtc.debugElement.componentViewChildren[0].nativeElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmpInstanceCount;
|
|
|
|
var childCmpInstanceCount;
|
|
|
|
|
|
|
|
export function main() {
|
|
|
|
describe('auxiliary routes', () => {
|
|
|
|
|
|
|
|
var tcb: TestComponentBuilder;
|
2015-12-02 14:54:10 -05:00
|
|
|
var fixture: ComponentFixture;
|
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 21:07:37 -05:00
|
|
|
var rtr;
|
|
|
|
|
|
|
|
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
|
|
|
|
|
|
|
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
|
|
|
tcb = tcBuilder;
|
|
|
|
rtr = router;
|
|
|
|
childCmpInstanceCount = 0;
|
|
|
|
cmpInstanceCount = 0;
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should recognize and navigate from the URL', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(tcb, `main {<router-outlet></router-outlet>} | aux {<router-outlet name="modal"></router-outlet>}`)
|
|
|
|
.then((rtc) => {fixture = rtc})
|
|
|
|
.then((_) => rtr.config([
|
|
|
|
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
|
|
|
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Aux'})
|
|
|
|
]))
|
|
|
|
.then((_) => rtr.navigateByUrl('/hello(modal)'))
|
|
|
|
.then((_) => {
|
|
|
|
fixture.detectChanges();
|
|
|
|
expect(fixture.debugElement.nativeElement).toHaveText('main {hello} | aux {modal}');
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should navigate via the link DSL', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(tcb, `main {<router-outlet></router-outlet>} | aux {<router-outlet name="modal"></router-outlet>}`)
|
|
|
|
.then((rtc) => {fixture = rtc})
|
|
|
|
.then((_) => rtr.config([
|
|
|
|
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
|
|
|
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Modal'})
|
|
|
|
]))
|
|
|
|
.then((_) => rtr.navigate(['/Hello', ['Modal']]))
|
|
|
|
.then((_) => {
|
|
|
|
fixture.detectChanges();
|
|
|
|
expect(fixture.debugElement.nativeElement).toHaveText('main {hello} | aux {modal}');
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
|
|
|
compile(
|
|
|
|
tcb,
|
|
|
|
`<a [router-link]="['/Hello', ['Modal']]">open modal</a> | main {<router-outlet></router-outlet>} | aux {<router-outlet name="modal"></router-outlet>}`)
|
|
|
|
.then((rtc) => {fixture = rtc})
|
|
|
|
.then((_) => rtr.config([
|
|
|
|
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
|
|
|
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Modal'})
|
|
|
|
]))
|
|
|
|
.then((_) => {
|
|
|
|
fixture.detectChanges();
|
|
|
|
expect(getHref(getLinkElement(fixture))).toEqual('/hello(modal)');
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should navigate from a link click',
|
|
|
|
inject([AsyncTestCompleter, Location], (async, location) => {
|
|
|
|
compile(
|
|
|
|
tcb,
|
|
|
|
`<a [router-link]="['/Hello', ['Modal']]">open modal</a> | main {<router-outlet></router-outlet>} | aux {<router-outlet name="modal"></router-outlet>}`)
|
|
|
|
.then((rtc) => {fixture = rtc})
|
|
|
|
.then((_) => rtr.config([
|
|
|
|
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
|
|
|
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Modal'})
|
|
|
|
]))
|
|
|
|
.then((_) => {
|
|
|
|
fixture.detectChanges();
|
|
|
|
expect(fixture.debugElement.nativeElement)
|
|
|
|
.toHaveText('open modal | main {} | aux {}');
|
|
|
|
|
|
|
|
rtr.subscribe((_) => {
|
|
|
|
fixture.detectChanges();
|
|
|
|
expect(fixture.debugElement.nativeElement)
|
|
|
|
.toHaveText('open modal | main {hello} | aux {modal}');
|
|
|
|
expect(location.urlChanges).toEqual(['/hello(modal)']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
|
|
|
|
clickOnElement(getLinkElement(fixture));
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Component({selector: 'hello-cmp', template: `{{greeting}}`})
|
|
|
|
class HelloCmp {
|
|
|
|
greeting: string;
|
|
|
|
constructor() { this.greeting = 'hello'; }
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({selector: 'modal-cmp', template: `modal`})
|
|
|
|
class ModalCmp {
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'aux-cmp',
|
|
|
|
template: 'main {<router-outlet></router-outlet>} | ' +
|
|
|
|
'aux {<router-outlet name="modal"></router-outlet>}',
|
|
|
|
directives: [ROUTER_DIRECTIVES],
|
|
|
|
})
|
|
|
|
@RouteConfig([
|
|
|
|
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
|
|
|
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Aux'})
|
|
|
|
])
|
|
|
|
class AuxCmp {
|
|
|
|
}
|