refactor(router): refactor BrowserLocation into LocationStrategy
This makes it easy to mock browser location and paves the way to implementing hash routing.
This commit is contained in:
parent
b48f000657
commit
e5de1f771a
|
@ -11,12 +11,14 @@ export {RouterOutlet} from './src/router/router_outlet';
|
||||||
export {RouterLink} from './src/router/router_link';
|
export {RouterLink} from './src/router/router_link';
|
||||||
export {RouteParams} from './src/router/instruction';
|
export {RouteParams} from './src/router/instruction';
|
||||||
export {RouteRegistry} from './src/router/route_registry';
|
export {RouteRegistry} from './src/router/route_registry';
|
||||||
export {BrowserLocation} from './src/router/browser_location';
|
export {LocationStrategy} from './src/router/location_strategy';
|
||||||
|
export {HTML5LocationStrategy} from './src/router/html5_location_strategy';
|
||||||
export {Location, appBaseHrefToken} from './src/router/location';
|
export {Location, appBaseHrefToken} from './src/router/location';
|
||||||
export {Pipeline} from './src/router/pipeline';
|
export {Pipeline} from './src/router/pipeline';
|
||||||
export * from './src/router/route_config_decorator';
|
export * from './src/router/route_config_decorator';
|
||||||
|
|
||||||
import {BrowserLocation} from './src/router/browser_location';
|
import {LocationStrategy} from './src/router/location_strategy';
|
||||||
|
import {HTML5LocationStrategy} from './src/router/html5_location_strategy';
|
||||||
import {Router, RootRouter} from './src/router/router';
|
import {Router, RootRouter} from './src/router/router';
|
||||||
import {RouterOutlet} from './src/router/router_outlet';
|
import {RouterOutlet} from './src/router/router_outlet';
|
||||||
import {RouterLink} from './src/router/router_link';
|
import {RouterLink} from './src/router/router_link';
|
||||||
|
@ -33,7 +35,7 @@ export const routerDirectives: List<any> = CONST_EXPR([RouterOutlet, RouterLink]
|
||||||
export var routerInjectables: List<any> = [
|
export var routerInjectables: List<any> = [
|
||||||
RouteRegistry,
|
RouteRegistry,
|
||||||
Pipeline,
|
Pipeline,
|
||||||
BrowserLocation,
|
bind(LocationStrategy).toClass(HTML5LocationStrategy),
|
||||||
Location,
|
Location,
|
||||||
bind(Router)
|
bind(Router)
|
||||||
.toFactory((registry, pipeline, location,
|
.toFactory((registry, pipeline, location,
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
import {proxy, SpyObject} from 'angular2/test_lib';
|
|
||||||
import {IMPLEMENTS, BaseException} from 'angular2/src/facade/lang';
|
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
import {List} from 'angular2/src/facade/collection';
|
||||||
import {BrowserLocation} from 'angular2/src/router/browser_location';
|
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||||
|
|
||||||
@proxy
|
|
||||||
@IMPLEMENTS(BrowserLocation)
|
export class MockLocationStrategy extends LocationStrategy {
|
||||||
export class DummyBrowserLocation extends SpyObject {
|
|
||||||
internalBaseHref: string = '/';
|
internalBaseHref: string = '/';
|
||||||
internalPath: string = '/';
|
internalPath: string = '/';
|
||||||
internalTitle: string = '';
|
internalTitle: string = '';
|
||||||
|
@ -31,13 +28,7 @@ export class DummyBrowserLocation extends SpyObject {
|
||||||
this.urlChanges.push(url);
|
this.urlChanges.push(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward(): void { throw new BaseException('Not implemented yet!'); }
|
|
||||||
|
|
||||||
back(): void { throw new BaseException('Not implemented yet!'); }
|
|
||||||
|
|
||||||
onPopState(fn): void { ObservableWrapper.subscribe(this._subject, fn); }
|
onPopState(fn): void { ObservableWrapper.subscribe(this._subject, fn); }
|
||||||
|
|
||||||
getBaseHref(): string { return this.internalBaseHref; }
|
getBaseHref(): string { return this.internalBaseHref; }
|
||||||
|
|
||||||
noSuchMethod(m) { return super.noSuchMethod(m); }
|
|
||||||
}
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {Injectable} from 'angular2/di';
|
import {Injectable} from 'angular2/di';
|
||||||
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
||||||
|
import {LocationStrategy} from './location_strategy';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class BrowserLocation {
|
export class HTML5LocationStrategy extends LocationStrategy {
|
||||||
private _location: Location;
|
private _location: Location;
|
||||||
private _history: History;
|
private _history: History;
|
||||||
private _baseHref: string;
|
private _baseHref: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this._location = DOM.getLocation();
|
this._location = DOM.getLocation();
|
||||||
this._history = DOM.getHistory();
|
this._history = DOM.getHistory();
|
||||||
this._baseHref = DOM.getBaseHref();
|
this._baseHref = DOM.getBaseHref();
|
|
@ -1,44 +1,57 @@
|
||||||
import {BrowserLocation} from './browser_location';
|
import {LocationStrategy} from './location_strategy';
|
||||||
import {StringWrapper, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
import {StringWrapper, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
import {OpaqueToken, Injectable, Optional, Inject} from 'angular2/di';
|
import {OpaqueToken, Injectable, Optional, Inject} from 'angular2/di';
|
||||||
|
|
||||||
export const appBaseHrefToken: OpaqueToken = CONST_EXPR(new OpaqueToken('locationHrefToken'));
|
export const appBaseHrefToken: OpaqueToken = CONST_EXPR(new OpaqueToken('locationHrefToken'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the service that an application developer will directly interact with.
|
||||||
|
*
|
||||||
|
* Responsible for normalizing the URL against the application's base href.
|
||||||
|
* A normalized URL is absolute from the URL host, includes the application's base href, and has no
|
||||||
|
* trailing slash:
|
||||||
|
* - `/my/app/user/123` is normalized
|
||||||
|
* - `my/app/user/123` **is not** normalized
|
||||||
|
* - `/my/app/user/123/` **is not** normalized
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Location {
|
export class Location {
|
||||||
private _subject: EventEmitter;
|
private _subject: EventEmitter;
|
||||||
private _baseHref: string;
|
private _baseHref: string;
|
||||||
|
|
||||||
constructor(public _browserLocation: BrowserLocation,
|
constructor(public _platformStrategy: LocationStrategy,
|
||||||
@Optional() @Inject(appBaseHrefToken) href?: string) {
|
@Optional() @Inject(appBaseHrefToken) href?: string) {
|
||||||
this._subject = new EventEmitter();
|
this._subject = new EventEmitter();
|
||||||
this._baseHref = stripIndexHtml(isPresent(href) ? href : this._browserLocation.getBaseHref());
|
this._baseHref = stripTrailingSlash(
|
||||||
this._browserLocation.onPopState((_) => this._onPopState(_));
|
stripIndexHtml(isPresent(href) ? href : this._platformStrategy.getBaseHref()));
|
||||||
|
this._platformStrategy.onPopState((_) => this._onPopState(_));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPopState(_): void { ObservableWrapper.callNext(this._subject, {'url': this.path()}); }
|
_onPopState(_): void { ObservableWrapper.callNext(this._subject, {'url': this.path()}); }
|
||||||
|
|
||||||
path(): string { return this.normalize(this._browserLocation.path()); }
|
path(): string { return this.normalize(this._platformStrategy.path()); }
|
||||||
|
|
||||||
normalize(url: string): string { return this._stripBaseHref(stripIndexHtml(url)); }
|
normalize(url: string): string {
|
||||||
|
return stripTrailingSlash(this._stripBaseHref(stripIndexHtml(url)));
|
||||||
|
}
|
||||||
|
|
||||||
normalizeAbsolutely(url: string): string {
|
normalizeAbsolutely(url: string): string {
|
||||||
if (url.length > 0 && url[0] != '/') {
|
if (!url.startsWith('/')) {
|
||||||
url = '/' + url;
|
url = '/' + url;
|
||||||
}
|
}
|
||||||
return this._addBaseHref(url);
|
return stripTrailingSlash(this._addBaseHref(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
_stripBaseHref(url: string): string {
|
_stripBaseHref(url: string): string {
|
||||||
if (this._baseHref.length > 0 && StringWrapper.startsWith(url, this._baseHref)) {
|
if (this._baseHref.length > 0 && url.startsWith(this._baseHref)) {
|
||||||
return StringWrapper.substring(url, this._baseHref.length);
|
return url.substring(this._baseHref.length);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addBaseHref(url: string): string {
|
_addBaseHref(url: string): string {
|
||||||
if (!StringWrapper.startsWith(url, this._baseHref)) {
|
if (!url.startsWith(this._baseHref)) {
|
||||||
return this._baseHref + url;
|
return this._baseHref + url;
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
|
@ -46,12 +59,12 @@ export class Location {
|
||||||
|
|
||||||
go(url: string): void {
|
go(url: string): void {
|
||||||
var finalUrl = this.normalizeAbsolutely(url);
|
var finalUrl = this.normalizeAbsolutely(url);
|
||||||
this._browserLocation.pushState(null, '', finalUrl);
|
this._platformStrategy.pushState(null, '', finalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward(): void { this._browserLocation.forward(); }
|
forward(): void { this._platformStrategy.forward(); }
|
||||||
|
|
||||||
back(): void { this._browserLocation.back(); }
|
back(): void { this._platformStrategy.back(); }
|
||||||
|
|
||||||
subscribe(onNext, onThrow = null, onReturn = null): void {
|
subscribe(onNext, onThrow = null, onReturn = null): void {
|
||||||
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||||
|
@ -61,12 +74,16 @@ export class Location {
|
||||||
|
|
||||||
|
|
||||||
function stripIndexHtml(url: string): string {
|
function stripIndexHtml(url: string): string {
|
||||||
// '/index.html'.length == 11
|
if (/\/index.html$/g.test(url)) {
|
||||||
if (url.length > 10 && StringWrapper.substring(url, url.length - 11) == '/index.html') {
|
// '/index.html'.length == 11
|
||||||
return StringWrapper.substring(url, 0, url.length - 11);
|
return url.substring(0, url.length - 11);
|
||||||
}
|
}
|
||||||
if (url.length > 1 && url[url.length - 1] == '/') {
|
return url;
|
||||||
url = StringWrapper.substring(url, 0, url.length - 1);
|
}
|
||||||
|
|
||||||
|
function stripTrailingSlash(url: string): string {
|
||||||
|
if (/\/$/g.test(url)) {
|
||||||
|
url = url.substring(0, url.length - 1);
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import {BaseException} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
function _abstract() {
|
||||||
|
return new BaseException('This method is abstract');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LocationStrategy {
|
||||||
|
path(): string { throw _abstract(); }
|
||||||
|
pushState(ctx: any, title: string, url: string): void { throw _abstract(); }
|
||||||
|
forward(): void { throw _abstract(); }
|
||||||
|
back(): void { throw _abstract(); }
|
||||||
|
onPopState(fn): void { throw _abstract(); }
|
||||||
|
getBaseHref(): string { throw _abstract(); }
|
||||||
|
}
|
|
@ -32,6 +32,8 @@ import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/even
|
||||||
|
|
||||||
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
|
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
|
||||||
import {MockXHR} from 'angular2/src/render/xhr_mock';
|
import {MockXHR} from 'angular2/src/render/xhr_mock';
|
||||||
|
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||||
|
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||||
|
|
||||||
import {TestBed} from './test_bed';
|
import {TestBed} from './test_bed';
|
||||||
|
@ -109,6 +111,7 @@ function _getAppBindings() {
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
ExceptionHandler,
|
ExceptionHandler,
|
||||||
|
bind(LocationStrategy).toClass(MockLocationStrategy),
|
||||||
bind(XHR).toClass(MockXHR),
|
bind(XHR).toClass(MockXHR),
|
||||||
ComponentUrlMapper,
|
ComponentUrlMapper,
|
||||||
UrlResolver,
|
UrlResolver,
|
||||||
|
|
|
@ -15,19 +15,19 @@ import {
|
||||||
import {Injector, bind} from 'angular2/di';
|
import {Injector, bind} from 'angular2/di';
|
||||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {Location, appBaseHrefToken} from 'angular2/src/router/location';
|
import {Location, appBaseHrefToken} from 'angular2/src/router/location';
|
||||||
import {BrowserLocation} from 'angular2/src/router/browser_location';
|
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||||
import {DummyBrowserLocation} from 'angular2/src/mock/browser_location_mock';
|
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Location', () => {
|
describe('Location', () => {
|
||||||
|
|
||||||
var browserLocation, location;
|
var locationStrategy, location;
|
||||||
|
|
||||||
function makeLocation(baseHref: string = '/my/app', binding: any = CONST_EXPR([])): Location {
|
function makeLocation(baseHref: string = '/my/app', binding: any = CONST_EXPR([])): Location {
|
||||||
browserLocation = new DummyBrowserLocation();
|
locationStrategy = new MockLocationStrategy();
|
||||||
browserLocation.internalBaseHref = baseHref;
|
locationStrategy.internalBaseHref = baseHref;
|
||||||
let injector = Injector.resolveAndCreate(
|
let injector = Injector.resolveAndCreate(
|
||||||
[Location, bind(BrowserLocation).toValue(browserLocation), binding]);
|
[Location, bind(LocationStrategy).toValue(locationStrategy), binding]);
|
||||||
return location = injector.get(Location);
|
return location = injector.get(Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,11 +35,11 @@ export function main() {
|
||||||
|
|
||||||
it('should normalize relative urls on navigate', () => {
|
it('should normalize relative urls on navigate', () => {
|
||||||
location.go('user/btford');
|
location.go('user/btford');
|
||||||
expect(browserLocation.path()).toEqual('/my/app/user/btford');
|
expect(locationStrategy.path()).toEqual('/my/app/user/btford');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not prepend urls with starting slash when an empty URL is provided',
|
it('should not prepend urls with starting slash when an empty URL is provided',
|
||||||
() => { expect(location.normalizeAbsolutely('')).toEqual(browserLocation.getBaseHref()); });
|
() => { expect(location.normalizeAbsolutely('')).toEqual(locationStrategy.getBaseHref()); });
|
||||||
|
|
||||||
it('should not prepend path with an extra slash when a baseHref has a trailing slash', () => {
|
it('should not prepend path with an extra slash when a baseHref has a trailing slash', () => {
|
||||||
let location = makeLocation('/my/slashed/app/');
|
let location = makeLocation('/my/slashed/app/');
|
||||||
|
@ -48,17 +48,17 @@ export function main() {
|
||||||
|
|
||||||
it('should not append urls with leading slash on navigate', () => {
|
it('should not append urls with leading slash on navigate', () => {
|
||||||
location.go('/my/app/user/btford');
|
location.go('/my/app/user/btford');
|
||||||
expect(browserLocation.path()).toEqual('/my/app/user/btford');
|
expect(locationStrategy.path()).toEqual('/my/app/user/btford');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove index.html from base href', () => {
|
it('should remove index.html from base href', () => {
|
||||||
let location = makeLocation('/my/app/index.html');
|
let location = makeLocation('/my/app/index.html');
|
||||||
location.go('user/btford');
|
location.go('user/btford');
|
||||||
expect(browserLocation.path()).toEqual('/my/app/user/btford');
|
expect(locationStrategy.path()).toEqual('/my/app/user/btford');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize urls on popstate', inject([AsyncTestCompleter], (async) => {
|
it('should normalize urls on popstate', inject([AsyncTestCompleter], (async) => {
|
||||||
browserLocation.simulatePopState('/my/app/user/btford');
|
locationStrategy.simulatePopState('/my/app/user/btford');
|
||||||
location.subscribe((ev) => {
|
location.subscribe((ev) => {
|
||||||
expect(ev['url']).toEqual('/user/btford');
|
expect(ev['url']).toEqual('/user/btford');
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -66,14 +66,14 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should normalize location path', () => {
|
it('should normalize location path', () => {
|
||||||
browserLocation.internalPath = '/my/app/user/btford';
|
locationStrategy.internalPath = '/my/app/user/btford';
|
||||||
expect(location.path()).toEqual('/user/btford');
|
expect(location.path()).toEqual('/user/btford');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use optional base href param', () => {
|
it('should use optional base href param', () => {
|
||||||
let location = makeLocation('/', bind(appBaseHrefToken).toValue('/my/custom/href'));
|
let location = makeLocation('/', bind(appBaseHrefToken).toValue('/my/custom/href'));
|
||||||
location.go('user/btford');
|
location.go('user/btford');
|
||||||
expect(browserLocation.path()).toEqual('/my/custom/href/user/btford');
|
expect(locationStrategy.path()).toEqual('/my/custom/href/user/btford');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@ import {RouteConfig} from 'angular2/src/router/route_config_decorator';
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {BaseException} from 'angular2/src/facade/lang';
|
import {BaseException} from 'angular2/src/facade/lang';
|
||||||
import {routerInjectables, Router, appBaseHrefToken, routerDirectives} from 'angular2/router';
|
import {routerInjectables, Router, appBaseHrefToken, routerDirectives} from 'angular2/router';
|
||||||
import {BrowserLocation} from 'angular2/src/router/browser_location';
|
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||||
import {DummyBrowserLocation} from 'angular2/src/mock/browser_location_mock';
|
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('router injectables', () => {
|
describe('router injectables', () => {
|
||||||
|
@ -32,12 +32,7 @@ export function main() {
|
||||||
DOM.appendChild(fakeDoc.body, el);
|
DOM.appendChild(fakeDoc.body, el);
|
||||||
testBindings = [
|
testBindings = [
|
||||||
routerInjectables,
|
routerInjectables,
|
||||||
bind(BrowserLocation)
|
bind(LocationStrategy).toClass(MockLocationStrategy),
|
||||||
.toFactory(() => {
|
|
||||||
var browserLocation = new DummyBrowserLocation();
|
|
||||||
browserLocation.spy('pushState');
|
|
||||||
return browserLocation;
|
|
||||||
}),
|
|
||||||
bind(DOCUMENT_TOKEN).toValue(fakeDoc)
|
bind(DOCUMENT_TOKEN).toValue(fakeDoc)
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
@ -48,7 +43,7 @@ export function main() {
|
||||||
var router = applicationRef.hostComponent.router;
|
var router = applicationRef.hostComponent.router;
|
||||||
router.subscribe((_) => {
|
router.subscribe((_) => {
|
||||||
expect(el).toHaveText('outer { hello }');
|
expect(el).toHaveText('outer { hello }');
|
||||||
expect(applicationRef.hostComponent.location.path()).toEqual('/');
|
expect(applicationRef.hostComponent.location.path()).toEqual('');
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -109,7 +104,7 @@ class HelloCmp {
|
||||||
@View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives})
|
@View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives})
|
||||||
@RouteConfig([{path: '/', component: HelloCmp}])
|
@RouteConfig([{path: '/', component: HelloCmp}])
|
||||||
class AppCmp {
|
class AppCmp {
|
||||||
constructor(public router: Router, public location: BrowserLocation) {}
|
constructor(public router: Router, public location: LocationStrategy) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,7 +119,7 @@ class ParentCmp {
|
||||||
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
|
@View({template: `root { <router-outlet></router-outlet> }`, directives: routerDirectives})
|
||||||
@RouteConfig([{path: '/parent/...', component: ParentCmp}])
|
@RouteConfig([{path: '/parent/...', component: ParentCmp}])
|
||||||
class HierarchyAppCmp {
|
class HierarchyAppCmp {
|
||||||
constructor(public router: Router, public location: BrowserLocation) {}
|
constructor(public router: Router, public location: LocationStrategy) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'oops-cmp'})
|
@Component({selector: 'oops-cmp'})
|
||||||
|
@ -137,5 +132,5 @@ class BrokenCmp {
|
||||||
@View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives})
|
@View({template: "outer { <router-outlet></router-outlet> }", directives: routerDirectives})
|
||||||
@RouteConfig([{path: '/cause-error', component: BrokenCmp}])
|
@RouteConfig([{path: '/cause-error', component: BrokenCmp}])
|
||||||
class BrokenAppCmp {
|
class BrokenAppCmp {
|
||||||
constructor(public router: Router, public location: BrowserLocation) {}
|
constructor(public router: Router, public location: LocationStrategy) {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue