Skip to content

Commit de37729

Browse files
committed
feat(router): implement Router.isRouteActive
1 parent e1a7e03 commit de37729

File tree

5 files changed

+223
-174
lines changed

5 files changed

+223
-174
lines changed

modules/angular1_router/src/ng_outlet.js

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -132,62 +132,41 @@ function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $
132132
}
133133
}
134134

135-
router.registerOutlet({
136-
commit: function (instruction) {
135+
router.registerPrimaryOutlet({
136+
reuse: function (instruction) {
137137
var next = $q.when(true);
138-
var componentInstruction = instruction.component;
139-
if (componentInstruction.reuse) {
140-
var previousInstruction = currentInstruction;
141-
currentInstruction = componentInstruction;
142-
if (currentController.onReuse) {
143-
next = $q.when(currentController.onReuse(currentInstruction, previousInstruction));
144-
}
145-
} else {
146-
var self = this;
147-
next = this.deactivate(instruction).then(function () {
148-
return self.activate(componentInstruction);
149-
});
138+
var previousInstruction = currentInstruction;
139+
currentInstruction = instruction;
140+
if (currentController.onReuse) {
141+
next = $q.when(currentController.onReuse(currentInstruction, previousInstruction));
150142
}
151-
return next.then(function () {
152-
if (childRouter) {
153-
return childRouter.commit(instruction.child);
154-
} else {
155-
return $q.when(true);
156-
}
157-
});
143+
144+
return next;
158145
},
159146
canReuse: function (nextInstruction) {
160147
var result;
161-
var componentInstruction = nextInstruction.component;
162148
if (!currentInstruction ||
163-
currentInstruction.componentType !== componentInstruction.componentType) {
149+
currentInstruction.componentType !== nextInstruction.componentType) {
164150
result = false;
165151
} else if (currentController.canReuse) {
166-
result = currentController.canReuse(componentInstruction, currentInstruction);
152+
result = currentController.canReuse(nextInstruction, currentInstruction);
167153
} else {
168-
result = componentInstruction === currentInstruction ||
169-
angular.equals(componentInstruction.params, currentInstruction.params);
154+
result = nextInstruction === currentInstruction ||
155+
angular.equals(nextInstruction.params, currentInstruction.params);
170156
}
171-
return $q.when(result).then(function (result) {
172-
// TODO: this is a hack
173-
componentInstruction.reuse = result;
174-
return result;
175-
});
157+
return $q.when(result);
176158
},
177159
canDeactivate: function (instruction) {
178160
if (currentInstruction && currentController && currentController.canDeactivate) {
179-
return $q.when(currentController.canDeactivate(instruction && instruction.component, currentInstruction));
161+
return $q.when(currentController.canDeactivate(instruction, currentInstruction));
180162
}
181163
return $q.when(true);
182164
},
183165
deactivate: function (instruction) {
184-
// todo(shahata): childRouter.dectivate, dispose component?
185-
var result = $q.when();
186-
return result.then(function () {
187-
if (currentController && currentController.onDeactivate) {
188-
return currentController.onDeactivate(instruction && instruction.component, currentInstruction);
189-
}
190-
});
166+
if (currentController && currentController.onDeactivate) {
167+
return $q.when(currentController.onDeactivate(instruction, currentInstruction));
168+
}
169+
return $q.when();
191170
},
192171
activate: function (instruction) {
193172
var previousInstruction = currentInstruction;
@@ -228,7 +207,7 @@ function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $
228207
}
229208
});
230209
}
231-
}, outletName);
210+
});
232211
}
233212
}
234213

modules/angular2/src/router/router.ts

Lines changed: 118 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,16 @@ export class Router {
5454
lastNavigationAttempt: string;
5555

5656
private _currentInstruction: Instruction = null;
57+
5758
private _currentNavigation: Promise<any> = _resolveToTrue;
5859
private _outlet: RouterOutlet = null;
59-
private _auxOutlets: Map<string, RouterOutlet> = new Map();
60+
61+
private _auxRouters: Map<string, Router> = new Map();
62+
private _childRouter: Router;
63+
6064
private _subject: EventEmitter = new EventEmitter();
6165

66+
6267
constructor(public registry: RouteRegistry, public _pipeline: Pipeline, public parent: Router,
6368
public hostComponent: any) {}
6469

@@ -67,26 +72,75 @@ export class Router {
6772
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
6873
* component.
6974
*/
70-
childRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); }
75+
childRouter(hostComponent: any): Router {
76+
return this._childRouter = new ChildRouter(this, hostComponent);
77+
}
78+
7179

80+
/**
81+
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
82+
* component.
83+
*/
84+
auxRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); }
7285

7386
/**
74-
* Register an object to notify of route changes. You probably don't need to use this unless
75-
* you're writing a reusable component.
87+
* Register an outlet to notified of primary route changes.
88+
*
89+
* You probably don't need to use this unless you're writing a reusable component.
7690
*/
77-
registerOutlet(outlet: RouterOutlet): Promise<boolean> {
91+
registerPrimaryOutlet(outlet: RouterOutlet): Promise<boolean> {
7892
if (isPresent(outlet.name)) {
79-
this._auxOutlets.set(outlet.name, outlet);
80-
} else {
81-
this._outlet = outlet;
93+
throw new BaseException(`registerAuxOutlet expects to be called with an unnamed outlet.`);
8294
}
95+
96+
this._outlet = outlet;
8397
if (isPresent(this._currentInstruction)) {
84-
return outlet.commit(this._currentInstruction);
98+
return this.commit(this._currentInstruction, false);
99+
}
100+
return _resolveToTrue;
101+
}
102+
103+
/**
104+
* Register an outlet to notified of auxiliary route changes.
105+
*
106+
* You probably don't need to use this unless you're writing a reusable component.
107+
*/
108+
registerAuxOutlet(outlet: RouterOutlet): Promise<boolean> {
109+
var outletName = outlet.name;
110+
if (isBlank(outletName)) {
111+
throw new BaseException(`registerAuxOutlet expects to be called with an outlet with a name.`);
112+
}
113+
114+
// TODO...
115+
// what is the host of an aux route???
116+
var router = this.auxRouter(this.hostComponent);
117+
118+
this._auxRouters.set(outletName, router);
119+
router._outlet = outlet;
120+
121+
var auxInstruction;
122+
if (isPresent(this._currentInstruction) &&
123+
isPresent(auxInstruction = this._currentInstruction.auxInstruction[outletName])) {
124+
return router.commit(auxInstruction);
85125
}
86126
return _resolveToTrue;
87127
}
88128

89129

130+
/**
131+
* Given an instruction, returns `true` if the instruction is currently active,
132+
* otherwise `false`.
133+
*/
134+
isRouteActive(instruction: Instruction): boolean {
135+
var router = this;
136+
while (isPresent(router.parent) && isPresent(instruction.child)) {
137+
router = router.parent;
138+
instruction = instruction.child;
139+
}
140+
return isPresent(this._currentInstruction) &&
141+
this._currentInstruction.component == instruction.component;
142+
}
143+
90144
/**
91145
* Dynamically update the routing configuration and trigger a navigation.
92146
*
@@ -143,7 +197,7 @@ export class Router {
143197

144198
_navigate(instruction: Instruction, _skipLocationChange: boolean): Promise<any> {
145199
return this._settleInstruction(instruction)
146-
.then((_) => this._reuse(instruction))
200+
.then((_) => this._canReuse(instruction))
147201
.then((_) => this._canActivate(instruction))
148202
.then((result) => {
149203
if (!result) {
@@ -190,14 +244,17 @@ export class Router {
190244
});
191245
}
192246

193-
_reuse(instruction: Instruction): Promise<any> {
247+
/*
248+
* Recursively set reuse flags
249+
*/
250+
_canReuse(instruction: Instruction): Promise<any> {
194251
if (isBlank(this._outlet)) {
195252
return _resolveToFalse;
196253
}
197-
return this._outlet.canReuse(instruction)
254+
return this._outlet.canReuse(instruction.component)
198255
.then((result) => {
199-
if (isPresent(this._outlet.childRouter) && isPresent(instruction.child)) {
200-
return this._outlet.childRouter._reuse(instruction.child);
256+
if (isPresent(this._childRouter) && isPresent(instruction.child)) {
257+
return this._childRouter._canReuse(instruction.child);
201258
}
202259
});
203260
}
@@ -211,19 +268,26 @@ export class Router {
211268
return _resolveToTrue;
212269
}
213270
var next: Promise<boolean>;
214-
if (isPresent(instruction) && instruction.component.reuse) {
271+
var childInstruction: Instruction = null;
272+
var reuse: boolean = false;
273+
var componentInstruction: ComponentInstruction = null;
274+
if (isPresent(instruction)) {
275+
childInstruction = instruction.child;
276+
componentInstruction = instruction.component;
277+
reuse = instruction.component.reuse;
278+
}
279+
if (reuse) {
215280
next = _resolveToTrue;
216281
} else {
217-
next = this._outlet.canDeactivate(instruction);
282+
next = this._outlet.canDeactivate(componentInstruction);
218283
}
219284
// TODO: aux route lifecycle hooks
220285
return next.then((result) => {
221286
if (result == false) {
222287
return false;
223288
}
224-
if (isPresent(this._outlet.childRouter)) {
225-
return this._outlet.childRouter._canDeactivate(isPresent(instruction) ? instruction.child :
226-
null);
289+
if (isPresent(this._childRouter)) {
290+
return this._childRouter._canDeactivate(childInstruction);
227291
}
228292
return true;
229293
});
@@ -234,13 +298,29 @@ export class Router {
234298
*/
235299
commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise<any> {
236300
this._currentInstruction = instruction;
237-
var next = _resolveToTrue;
301+
var next: Promise<any> = _resolveToTrue;
238302
if (isPresent(this._outlet)) {
239-
next = this._outlet.commit(instruction);
303+
var componentInstruction = instruction.component;
304+
if (componentInstruction.reuse) {
305+
next = this._outlet.reuse(componentInstruction);
306+
} else {
307+
next =
308+
this.deactivate(instruction).then((_) => this._outlet.activate(componentInstruction));
309+
}
310+
if (isPresent(instruction.child)) {
311+
next = next.then((_) => {
312+
if (isPresent(this._childRouter)) {
313+
return this._childRouter.commit(instruction.child);
314+
}
315+
});
316+
}
240317
}
318+
241319
var promises = [];
242-
MapWrapper.forEach(this._auxOutlets,
243-
(outlet, _) => { promises.push(outlet.commit(instruction)); });
320+
MapWrapper.forEach(this._auxRouters, (router, name) => {
321+
promises.push(router.commit(instruction.auxInstruction[name]));
322+
});
323+
244324
return next.then((_) => PromiseWrapper.all(promises));
245325
}
246326

@@ -262,10 +342,23 @@ export class Router {
262342
* Removes the contents of this router's outlet and all descendant outlets
263343
*/
264344
deactivate(instruction: Instruction): Promise<any> {
345+
var childInstruction: Instruction = null;
346+
var componentInstruction: ComponentInstruction = null;
347+
if (isPresent(instruction)) {
348+
childInstruction = instruction.child;
349+
componentInstruction = instruction.component;
350+
}
351+
var next: Promise<any> = _resolveToTrue;
352+
if (isPresent(this._childRouter)) {
353+
next = this._childRouter.deactivate(childInstruction);
354+
}
265355
if (isPresent(this._outlet)) {
266-
return this._outlet.deactivate(instruction);
356+
next = next.then((_) => this._outlet.deactivate(componentInstruction));
267357
}
268-
return _resolveToTrue;
358+
359+
// TODO: handle aux routes
360+
361+
return next;
269362
}
270363

271364

0 commit comments

Comments
 (0)