Skip to content

Commit ea546f5

Browse files
committed
feat(router): add location service
1 parent cf32213 commit ea546f5

File tree

5 files changed

+149
-10
lines changed

5 files changed

+149
-10
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import {SpyObject, proxy} from 'angular2/test_lib';
2+
3+
import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang';
4+
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
5+
import {List, ListWrapper} from 'angular2/src/facade/collection';
6+
import {Location} from 'angular2/src/router/location';
7+
8+
9+
@proxy
10+
@IMPLEMENTS(Location)
11+
export class DummyLocation extends SpyObject {
12+
urlChanges:List<string>;
13+
_path:string;
14+
_subject:EventEmitter;
15+
16+
constructor() {
17+
super();
18+
this._path = '/';
19+
this.urlChanges = ListWrapper.create();
20+
this._subject = new EventEmitter();
21+
}
22+
23+
setInitialPath(url:string) {
24+
this._path = url;
25+
}
26+
27+
path():string {
28+
return this._path;
29+
}
30+
31+
simulateUrlPop(pathname:string) {
32+
ObservableWrapper.callNext(this._subject, {
33+
'url': pathname
34+
});
35+
}
36+
37+
go(url:string) {
38+
if (this._path === url) {
39+
return;
40+
}
41+
this._path = url;
42+
ListWrapper.push(this.urlChanges, url);
43+
}
44+
45+
forward() {
46+
// TODO
47+
}
48+
49+
back() {
50+
// TODO
51+
}
52+
53+
subscribe(onNext, onThrow = null, onReturn = null) {
54+
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
55+
}
56+
57+
noSuchMethod(m){return super.noSuchMethod(m);}
58+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {global} from 'angular2/src/facade/lang';
2+
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
3+
4+
export class Location {
5+
_location;
6+
_subject:EventEmitter;
7+
_history;
8+
constructor() {
9+
this._subject = new EventEmitter();
10+
this._location = global.location;
11+
this._history = global.history;
12+
global.addEventListener('popstate', (_) => this._onPopState(_), false);
13+
}
14+
15+
_onPopState(_) {
16+
ObservableWrapper.callNext(this._subject, {
17+
'url': this._location.pathname
18+
});
19+
}
20+
21+
path() {
22+
return this._location.pathname;
23+
}
24+
25+
go(url:string) {
26+
this._history.pushState(null, null, url);
27+
}
28+
29+
forward() {
30+
this._history.forward();
31+
}
32+
33+
back() {
34+
this._history.back()
35+
}
36+
37+
subscribe(onNext, onThrow = null, onReturn = null) {
38+
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
39+
}
40+
}

modules/angular2/src/router/router.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {RouteRegistry} from './route_registry';
66
import {Pipeline} from './pipeline';
77
import {Instruction} from './instruction';
88
import {RouterOutlet} from './router_outlet';
9+
import {Location} from './location';
910

1011
/**
1112
* # Router
@@ -28,17 +29,21 @@ export class Router {
2829
_outlets:Map<any, RouterOutlet>;
2930
_children:Map<any, Router>;
3031
_subject:EventEmitter;
31-
32-
constructor(registry:RouteRegistry, pipeline:Pipeline, parent:Router = null, name = '/') {
32+
_location:Location;
33+
34+
constructor(registry:RouteRegistry, pipeline:Pipeline, location:Location, parent:Router = null, name = '/') {
3335
this.name = name;
3436
this.navigating = false;
3537
this.parent = parent;
3638
this.previousUrl = null;
3739
this._outlets = MapWrapper.create();
3840
this._children = MapWrapper.create();
41+
this._location = location;
3942
this._registry = registry;
4043
this._pipeline = pipeline;
4144
this._subject = new EventEmitter();
45+
this._location.subscribe((url) => this.navigate(url));
46+
this.navigate(location.path());
4247
}
4348

4449

@@ -97,6 +102,9 @@ export class Router {
97102
this._startNavigating();
98103

99104
var result = this._pipeline.process(instruction)
105+
.then((_) => {
106+
this._location.go(instruction.matchedUrl);
107+
})
100108
.then((_) => {
101109
ObservableWrapper.callNext(this._subject, instruction.matchedUrl);
102110
})
@@ -170,19 +178,19 @@ export class Router {
170178
}
171179

172180
static getRoot():Router {
173-
return new RootRouter(new Pipeline());
181+
return new RootRouter(new Pipeline(), new Location());
174182
}
175183
}
176184

177185
export class RootRouter extends Router {
178-
constructor(pipeline:Pipeline) {
179-
super(new RouteRegistry(), pipeline, null, '/');
186+
constructor(pipeline:Pipeline, location:Location) {
187+
super(new RouteRegistry(), pipeline, location, null, '/');
180188
}
181189
}
182190

183191
class ChildRouter extends Router {
184192
constructor(parent, name) {
185-
super(parent._registry, parent._pipeline, parent, name);
193+
super(parent._registry, parent._pipeline, parent._location, parent, name);
186194
this.parent = parent;
187195
}
188196
}

modules/angular2/test/router/outlet_spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {Router, RouterOutlet, RouterLink, RouteConfig, RouteParams} from 'angula
2525

2626
import {DOM} from 'angular2/src/dom/dom_adapter';
2727

28+
import {DummyLocation} from 'angular2/src/mock/location_mock';
29+
2830
export function main() {
2931
describe('Outlet Directive', () => {
3032

@@ -36,7 +38,7 @@ export function main() {
3638
}));
3739

3840
beforeEachBindings(() => {
39-
router = new RootRouter(new Pipeline());
41+
router = new RootRouter(new Pipeline(), new DummyLocation());
4042
return [
4143
bind(Router).toValue(router)
4244
];

modules/angular2/test/router/router_spec.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,47 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
1212
import {RootRouter} from 'angular2/src/router/router';
1313
import {Pipeline} from 'angular2/src/router/pipeline';
1414
import {RouterOutlet} from 'angular2/src/router/router_outlet';
15-
15+
import {DummyLocation} from 'angular2/src/mock/location_mock'
1616

1717
export function main() {
1818
describe('Router', () => {
19-
var router;
19+
var router,
20+
location;
2021

2122
beforeEach(() => {
22-
router = new RootRouter(new Pipeline());
23+
location = new DummyLocation();
24+
router = new RootRouter(new Pipeline(), location);
2325
});
2426

27+
28+
it('should navigate based on the initial URL state', inject([AsyncTestCompleter], (async) => {
29+
var outlet = makeDummyRef();
30+
31+
router.config('/', {'component': 'Index' })
32+
.then((_) => router.registerOutlet(outlet))
33+
.then((_) => {
34+
expect(outlet.spy('activate')).toHaveBeenCalled();
35+
expect(location.urlChanges).toEqual(['/']);
36+
async.done();
37+
});
38+
}));
39+
40+
41+
it('should activate viewports and update URL on navigate', inject([AsyncTestCompleter], (async) => {
42+
var outlet = makeDummyRef();
43+
44+
router.registerOutlet(outlet)
45+
.then((_) => {
46+
return router.config('/a', {'component': 'A' });
47+
})
48+
.then((_) => router.navigate('/a'))
49+
.then((_) => {
50+
expect(outlet.spy('activate')).toHaveBeenCalled();
51+
expect(location.urlChanges).toEqual(['/a']);
52+
async.done();
53+
});
54+
}));
55+
2556
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
2657
var outlet = makeDummyRef();
2758

0 commit comments

Comments
 (0)