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 {Instruction} from './instruction';
|
||||
import {RouterOutlet} from './router_outlet';
|
||||
import {Location} from './location';
|
||||
|
||||
/**
|
||||
* # Router
|
||||
|
@ -28,17 +29,21 @@ export class Router {
|
|||
_outlets:Map<any, RouterOutlet>;
|
||||
_children:Map<any, Router>;
|
||||
_subject:EventEmitter;
|
||||
|
||||
constructor(registry:RouteRegistry, pipeline:Pipeline, parent:Router = null, name = '/') {
|
||||
_location:Location;
|
||||
|
||||
constructor(registry:RouteRegistry, pipeline:Pipeline, location:Location, parent:Router = null, name = '/') {
|
||||
this.name = name;
|
||||
this.navigating = false;
|
||||
this.parent = parent;
|
||||
this.previousUrl = null;
|
||||
this._outlets = MapWrapper.create();
|
||||
this._children = MapWrapper.create();
|
||||
this._location = location;
|
||||
this._registry = registry;
|
||||
this._pipeline = pipeline;
|
||||
this._subject = new EventEmitter();
|
||||
this._location.subscribe((url) => this.navigate(url));
|
||||
this.navigate(location.path());
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,6 +102,9 @@ export class Router {
|
|||
this._startNavigating();
|
||||
|
||||
var result = this._pipeline.process(instruction)
|
||||
.then((_) => {
|
||||
this._location.go(instruction.matchedUrl);
|
||||
})
|
||||
.then((_) => {
|
||||
ObservableWrapper.callNext(this._subject, instruction.matchedUrl);
|
||||
})
|
||||
|
@ -170,19 +178,19 @@ export class Router {
|
|||
}
|
||||
|
||||
static getRoot():Router {
|
||||
return new RootRouter(new Pipeline());
|
||||
return new RootRouter(new Pipeline(), new Location());
|
||||
}
|
||||
}
|
||||
|
||||
export class RootRouter extends Router {
|
||||
constructor(pipeline:Pipeline) {
|
||||
super(new RouteRegistry(), pipeline, null, '/');
|
||||
constructor(pipeline:Pipeline, location:Location) {
|
||||
super(new RouteRegistry(), pipeline, location, null, '/');
|
||||
}
|
||||
}
|
||||
|
||||
class ChildRouter extends Router {
|
||||
constructor(parent, name) {
|
||||
super(parent._registry, parent._pipeline, parent, name);
|
||||
super(parent._registry, parent._pipeline, parent._location, parent, name);
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import {Router, RouterOutlet, RouterLink, RouteConfig, RouteParams} from 'angula
|
|||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {DummyLocation} from 'angular2/src/mock/location_mock';
|
||||
|
||||
export function main() {
|
||||
describe('Outlet Directive', () => {
|
||||
|
||||
|
@ -36,7 +38,7 @@ export function main() {
|
|||
}));
|
||||
|
||||
beforeEachBindings(() => {
|
||||
router = new RootRouter(new Pipeline());
|
||||
router = new RootRouter(new Pipeline(), new DummyLocation());
|
||||
return [
|
||||
bind(Router).toValue(router)
|
||||
];
|
||||
|
|
|
@ -12,16 +12,47 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|||
import {RootRouter} from 'angular2/src/router/router';
|
||||
import {Pipeline} from 'angular2/src/router/pipeline';
|
||||
import {RouterOutlet} from 'angular2/src/router/router_outlet';
|
||||
|
||||
import {DummyLocation} from 'angular2/src/mock/location_mock'
|
||||
|
||||
export function main() {
|
||||
describe('Router', () => {
|
||||
var router;
|
||||
var router,
|
||||
location;
|
||||
|
||||
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) => {
|
||||
var outlet = makeDummyRef();
|
||||
|
||||
|
|
Loading…
Reference in New Issue