feat(router): add reuse support for angular 1.x router

Closes #3698
This commit is contained in:
Shahar Talmi 2015-08-18 01:31:41 +03:00 committed by Brian Ford
parent fde026a9e4
commit ddb62feae6
3 changed files with 89 additions and 5 deletions

View File

@ -1,3 +1,5 @@
'use strict';
var fs = require('fs'); var fs = require('fs');
var ts = require('typescript'); var ts = require('typescript');
@ -58,6 +60,10 @@ function main() {
"});", "});",
"}));", "}));",
"}", "}",
"if (constructor.$routeConfig) {",
"constructor.annotations = constructor.annotations || [];",
"constructor.annotations.push(new angular.annotations.RouteConfig(constructor.$routeConfig));",
"}",
"if (constructor.annotations) {", "if (constructor.annotations) {",
"constructor.annotations.forEach(function(annotation) {", "constructor.annotations.forEach(function(annotation) {",
"if (annotation instanceof RouteConfig) {", "if (annotation instanceof RouteConfig) {",
@ -70,7 +76,11 @@ function main() {
"});", "});",
"var router = new RootRouter(registry, undefined, location, new Object());", "var router = new RootRouter(registry, undefined, location, new Object());",
"$rootScope.$watch(function () { return $location.path(); }, function (path) { router.navigate(path); });", "$rootScope.$watch(function () { return $location.path(); }, function (path) {",
"if (router.lastNavigationAttempt !== path) {",
"router.navigate(path);",
"}",
"});",
"return router;" "return router;"
].join('\n')); ].join('\n'));

View File

@ -134,11 +134,14 @@ function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $
router.registerOutlet({ router.registerOutlet({
commit: function (instruction) { commit: function (instruction) {
var next; var next = $q.when(true);
var componentInstruction = instruction.component; var componentInstruction = instruction.component;
if (componentInstruction.reuse) { if (componentInstruction.reuse) {
// todo(shahata): lifecycle - onReuse var previousInstruction = currentInstruction;
next = $q.when(true); currentInstruction = componentInstruction;
if (currentController.onReuse) {
next = $q.when(currentController.onReuse(currentInstruction, previousInstruction));
}
} else { } else {
var self = this; var self = this;
next = this.deactivate(instruction).then(function () { next = this.deactivate(instruction).then(function () {
@ -159,8 +162,9 @@ function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $
if (!currentInstruction || if (!currentInstruction ||
currentInstruction.componentType !== componentInstruction.componentType) { currentInstruction.componentType !== componentInstruction.componentType) {
result = false; result = false;
} else if (currentController.canReuse) {
result = currentController.canReuse(componentInstruction, currentInstruction);
} else { } else {
// todo(shahata): lifecycle - canReuse
result = componentInstruction === currentInstruction || result = componentInstruction === currentInstruction ||
angular.equals(componentInstruction.params, currentInstruction.params); angular.equals(componentInstruction.params, currentInstruction.params);
} }

View File

@ -358,6 +358,76 @@ describe('ngOutlet', function () {
}); });
it('should reuse a component when the canReuse hook returns true', function () {
var log = [];
var cmpInstanceCount = 0;
function ReuseCmp() {
cmpInstanceCount++;
this.canReuse = function () {
return true;
};
this.onReuse = function (next, prev) {
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
};
}
ReuseCmp.$routeConfig = [{path: '/a', component: OneController}, {path: '/b', component: TwoController}];
registerComponent('reuse', 'reuse {<ng-outlet></ng-outlet>}', ReuseCmp);
$router.config([
{ path: '/on-reuse/:number/...', component: ReuseCmp }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigate('/on-reuse/1/a');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {one} }');
$router.navigate('/on-reuse/2/b');
$rootScope.$digest();
expect(log).toEqual(['reuse: on-reuse/1 -> on-reuse/2']);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {two} }');
});
it('should not reuse a component when the canReuse hook returns false', function () {
var log = [];
var cmpInstanceCount = 0;
function NeverReuseCmp() {
cmpInstanceCount++;
this.canReuse = function () {
return false;
};
this.onReuse = function (next, prev) {
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
};
}
NeverReuseCmp.$routeConfig = [{path: '/a', component: OneController}, {path: '/b', component: TwoController}];
registerComponent('reuse', 'reuse {<ng-outlet></ng-outlet>}', NeverReuseCmp);
$router.config([
{ path: '/never-reuse/:number/...', component: NeverReuseCmp }
]);
compile('outer { <div ng-outlet></div> }');
$router.navigate('/never-reuse/1/a');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(1);
expect(elt.text()).toBe('outer { reuse {one} }');
$router.navigate('/never-reuse/2/b');
$rootScope.$digest();
expect(log).toEqual([]);
expect(cmpInstanceCount).toBe(2);
expect(elt.text()).toBe('outer { reuse {two} }');
});
it('should not activate a component when canActivate returns false', function () { it('should not activate a component when canActivate returns false', function () {
var spy = jasmine.createSpy('activate'); var spy = jasmine.createSpy('activate');
var activate = registerComponent('activate', '', { var activate = registerComponent('activate', '', {