feat(router): add location service
This commit is contained in:
parent
cf32213079
commit
ea546f5069
|
@ -0,0 +1,58 @@
|
||||||
|
import {SpyObject, proxy} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||||
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {Location} from 'angular2/src/router/location';
|
||||||
|
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(Location)
|
||||||
|
export class DummyLocation extends SpyObject {
|
||||||
|
urlChanges:List<string>;
|
||||||
|
_path:string;
|
||||||
|
_subject:EventEmitter;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._path = '/';
|
||||||
|
this.urlChanges = ListWrapper.create();
|
||||||
|
this._subject = new EventEmitter();
|
||||||
|
}
|
||||||
|
|
||||||
|
setInitialPath(url:string) {
|
||||||
|
this._path = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
path():string {
|
||||||
|
return this._path;
|
||||||
|
}
|
||||||
|
|
||||||
|
simulateUrlPop(pathname:string) {
|
||||||
|
ObservableWrapper.callNext(this._subject, {
|
||||||
|
'url': pathname
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
go(url:string) {
|
||||||
|
if (this._path === url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._path = url;
|
||||||
|
ListWrapper.push(this.urlChanges, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
forward() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
back() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(onNext, onThrow = null, onReturn = null) {
|
||||||
|
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(m){return super.noSuchMethod(m);}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {global} from 'angular2/src/facade/lang';
|
||||||
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
|
export class Location {
|
||||||
|
_location;
|
||||||
|
_subject:EventEmitter;
|
||||||
|
_history;
|
||||||
|
constructor() {
|
||||||
|
this._subject = new EventEmitter();
|
||||||
|
this._location = global.location;
|
||||||
|
this._history = global.history;
|
||||||
|
global.addEventListener('popstate', (_) => this._onPopState(_), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPopState(_) {
|
||||||
|
ObservableWrapper.callNext(this._subject, {
|
||||||
|
'url': this._location.pathname
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
return this._location.pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
go(url:string) {
|
||||||
|
this._history.pushState(null, null, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
forward() {
|
||||||
|
this._history.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
back() {
|
||||||
|
this._history.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(onNext, onThrow = null, onReturn = null) {
|
||||||
|
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import {RouteRegistry} from './route_registry';
|
||||||
import {Pipeline} from './pipeline';
|
import {Pipeline} from './pipeline';
|
||||||
import {Instruction} from './instruction';
|
import {Instruction} from './instruction';
|
||||||
import {RouterOutlet} from './router_outlet';
|
import {RouterOutlet} from './router_outlet';
|
||||||
|
import {Location} from './location';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* # Router
|
* # Router
|
||||||
|
@ -28,17 +29,21 @@ export class Router {
|
||||||
_outlets:Map<any, RouterOutlet>;
|
_outlets:Map<any, RouterOutlet>;
|
||||||
_children:Map<any, Router>;
|
_children:Map<any, Router>;
|
||||||
_subject:EventEmitter;
|
_subject:EventEmitter;
|
||||||
|
_location:Location;
|
||||||
constructor(registry:RouteRegistry, pipeline:Pipeline, parent:Router = null, name = '/') {
|
|
||||||
|
constructor(registry:RouteRegistry, pipeline:Pipeline, location:Location, parent:Router = null, name = '/') {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.navigating = false;
|
this.navigating = false;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.previousUrl = null;
|
this.previousUrl = null;
|
||||||
this._outlets = MapWrapper.create();
|
this._outlets = MapWrapper.create();
|
||||||
this._children = MapWrapper.create();
|
this._children = MapWrapper.create();
|
||||||
|
this._location = location;
|
||||||
this._registry = registry;
|
this._registry = registry;
|
||||||
this._pipeline = pipeline;
|
this._pipeline = pipeline;
|
||||||
this._subject = new EventEmitter();
|
this._subject = new EventEmitter();
|
||||||
|
this._location.subscribe((url) => this.navigate(url));
|
||||||
|
this.navigate(location.path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,6 +102,9 @@ export class Router {
|
||||||
this._startNavigating();
|
this._startNavigating();
|
||||||
|
|
||||||
var result = this._pipeline.process(instruction)
|
var result = this._pipeline.process(instruction)
|
||||||
|
.then((_) => {
|
||||||
|
this._location.go(instruction.matchedUrl);
|
||||||
|
})
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
ObservableWrapper.callNext(this._subject, instruction.matchedUrl);
|
ObservableWrapper.callNext(this._subject, instruction.matchedUrl);
|
||||||
})
|
})
|
||||||
|
@ -170,19 +178,19 @@ export class Router {
|
||||||
}
|
}
|
||||||
|
|
||||||
static getRoot():Router {
|
static getRoot():Router {
|
||||||
return new RootRouter(new Pipeline());
|
return new RootRouter(new Pipeline(), new Location());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RootRouter extends Router {
|
export class RootRouter extends Router {
|
||||||
constructor(pipeline:Pipeline) {
|
constructor(pipeline:Pipeline, location:Location) {
|
||||||
super(new RouteRegistry(), pipeline, null, '/');
|
super(new RouteRegistry(), pipeline, location, null, '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChildRouter extends Router {
|
class ChildRouter extends Router {
|
||||||
constructor(parent, name) {
|
constructor(parent, name) {
|
||||||
super(parent._registry, parent._pipeline, parent, name);
|
super(parent._registry, parent._pipeline, parent._location, parent, name);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ import {Router, RouterOutlet, RouterLink, RouteConfig, RouteParams} from 'angula
|
||||||
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
import {DummyLocation} from 'angular2/src/mock/location_mock';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Outlet Directive', () => {
|
describe('Outlet Directive', () => {
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEachBindings(() => {
|
beforeEachBindings(() => {
|
||||||
router = new RootRouter(new Pipeline());
|
router = new RootRouter(new Pipeline(), new DummyLocation());
|
||||||
return [
|
return [
|
||||||
bind(Router).toValue(router)
|
bind(Router).toValue(router)
|
||||||
];
|
];
|
||||||
|
|
|
@ -12,16 +12,47 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {RootRouter} from 'angular2/src/router/router';
|
import {RootRouter} from 'angular2/src/router/router';
|
||||||
import {Pipeline} from 'angular2/src/router/pipeline';
|
import {Pipeline} from 'angular2/src/router/pipeline';
|
||||||
import {RouterOutlet} from 'angular2/src/router/router_outlet';
|
import {RouterOutlet} from 'angular2/src/router/router_outlet';
|
||||||
|
import {DummyLocation} from 'angular2/src/mock/location_mock'
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Router', () => {
|
describe('Router', () => {
|
||||||
var router;
|
var router,
|
||||||
|
location;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
router = new RootRouter(new Pipeline());
|
location = new DummyLocation();
|
||||||
|
router = new RootRouter(new Pipeline(), location);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should navigate based on the initial URL state', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var outlet = makeDummyRef();
|
||||||
|
|
||||||
|
router.config('/', {'component': 'Index' })
|
||||||
|
.then((_) => router.registerOutlet(outlet))
|
||||||
|
.then((_) => {
|
||||||
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
|
expect(location.urlChanges).toEqual(['/']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should activate viewports and update URL on navigate', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var outlet = makeDummyRef();
|
||||||
|
|
||||||
|
router.registerOutlet(outlet)
|
||||||
|
.then((_) => {
|
||||||
|
return router.config('/a', {'component': 'A' });
|
||||||
|
})
|
||||||
|
.then((_) => router.navigate('/a'))
|
||||||
|
.then((_) => {
|
||||||
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
|
expect(location.urlChanges).toEqual(['/a']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
|
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
|
||||||
var outlet = makeDummyRef();
|
var outlet = makeDummyRef();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue