refactor(angular_1_router): use directives for route targets
BREAKING CHANGE: Previously, route configuration took a controller constructor function as the value of `component` in a route definition: ``` $route.config([ { route: '/', component: MyController } ]) ``` Based on the name of the controller, we used to use a componentMapper service to determine what template to pair with each controller, how to bind the instance to the $scope. To make the 1.x router more semantically alligned with Angular 2, we now route to a directive. Thus a route configuration takes a normalized directive name: ``` $route.config([ { route: '/', component: 'myDirective' } ]) ``` BREAKING CHANGE: In order to avoid name collisions, lifecycle hooks are now prefixed with `$`. Before: ``` MyController.prototype.onActivate = ... ``` After: ``` MyController.prototype.$onActivate = ... ``` Same for `$canActivate` (which now lives on the directive factory function), `$canDeactivate`, `$canReuse`, and `$onDeactivate` hooks.
This commit is contained in:
parent
6e0ca7f39a
commit
5205a9e65f
|
@ -22,12 +22,12 @@ var PRELUDE = '(function(){\n';
|
||||||
var POSTLUDE = '\n}());\n';
|
var POSTLUDE = '\n}());\n';
|
||||||
var FACADES = fs.readFileSync(__dirname + '/lib/facades.es5', 'utf8');
|
var FACADES = fs.readFileSync(__dirname + '/lib/facades.es5', 'utf8');
|
||||||
var DIRECTIVES = fs.readFileSync(__dirname + '/src/ng_outlet.js', 'utf8');
|
var DIRECTIVES = fs.readFileSync(__dirname + '/src/ng_outlet.js', 'utf8');
|
||||||
|
var moduleTemplate = fs.readFileSync(__dirname + '/src/module_template.js', 'utf8');
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
var ES6_SHIM = fs.readFileSync(__dirname + '/../../node_modules/es6-shim/es6-shim.js', 'utf8');
|
var ES6_SHIM = fs.readFileSync(__dirname + '/../../node_modules/es6-shim/es6-shim.js', 'utf8');
|
||||||
var dir = __dirname + '/../angular2/src/router/';
|
var dir = __dirname + '/../angular2/src/router/';
|
||||||
|
|
||||||
var out = '';
|
|
||||||
|
|
||||||
var sharedCode = '';
|
var sharedCode = '';
|
||||||
files.forEach(function (file) {
|
files.forEach(function (file) {
|
||||||
var moduleName = 'router/' + file.replace(/\.ts$/, '');
|
var moduleName = 'router/' + file.replace(/\.ts$/, '');
|
||||||
|
@ -35,57 +35,9 @@ function main() {
|
||||||
sharedCode += transform(moduleName, fs.readFileSync(dir + file, 'utf8'));
|
sharedCode += transform(moduleName, fs.readFileSync(dir + file, 'utf8'));
|
||||||
});
|
});
|
||||||
|
|
||||||
out += "angular.module('ngComponentRouter')";
|
var out = moduleTemplate.replace('//{{FACADES}}', FACADES).replace('//{{SHARED_CODE}}', sharedCode);
|
||||||
out += angularFactory('$router', ['$q', '$location', '$$controllerIntrospector',
|
|
||||||
'$browser', '$rootScope', '$injector'], [
|
|
||||||
FACADES,
|
|
||||||
"var exports = {Injectable: function () {}};",
|
|
||||||
"var require = function () {return exports;};",
|
|
||||||
sharedCode,
|
|
||||||
"var RouteConfig = exports.RouteConfig;",
|
|
||||||
"angular.annotations = {RouteConfig: RouteConfig, CanActivate: exports.CanActivate};",
|
|
||||||
"angular.stringifyInstruction = exports.stringifyInstruction;",
|
|
||||||
"var RouteRegistry = exports.RouteRegistry;",
|
|
||||||
"var RootRouter = exports.RootRouter;",
|
|
||||||
//TODO: move this code into a templated JS file
|
|
||||||
"var registry = new RouteRegistry();",
|
|
||||||
"var location = new Location();",
|
|
||||||
|
|
||||||
"$$controllerIntrospector(function (name, constructor) {",
|
return PRELUDE + DIRECTIVES + out + POSTLUDE;
|
||||||
"if (constructor.$canActivate) {",
|
|
||||||
"constructor.annotations = constructor.annotations || [];",
|
|
||||||
"constructor.annotations.push(new angular.annotations.CanActivate(function (instruction) {",
|
|
||||||
"return $injector.invoke(constructor.$canActivate, constructor, {",
|
|
||||||
"$routeParams: instruction.component ? instruction.component.params : instruction.params",
|
|
||||||
"});",
|
|
||||||
"}));",
|
|
||||||
"}",
|
|
||||||
"if (constructor.$routeConfig) {",
|
|
||||||
"constructor.annotations = constructor.annotations || [];",
|
|
||||||
"constructor.annotations.push(new angular.annotations.RouteConfig(constructor.$routeConfig));",
|
|
||||||
"}",
|
|
||||||
"if (constructor.annotations) {",
|
|
||||||
"constructor.annotations.forEach(function(annotation) {",
|
|
||||||
"if (annotation instanceof RouteConfig) {",
|
|
||||||
"annotation.configs.forEach(function (config) {",
|
|
||||||
"registry.config(constructor, config);",
|
|
||||||
"});",
|
|
||||||
"}",
|
|
||||||
"});",
|
|
||||||
"}",
|
|
||||||
"});",
|
|
||||||
|
|
||||||
"var router = new RootRouter(registry, location, new Object());",
|
|
||||||
"$rootScope.$watch(function () { return $location.path(); }, function (path) {",
|
|
||||||
"if (router.lastNavigationAttempt !== path) {",
|
|
||||||
"router.navigateByUrl(path);",
|
|
||||||
"}",
|
|
||||||
"});",
|
|
||||||
|
|
||||||
"return router;"
|
|
||||||
].join('\n'));
|
|
||||||
|
|
||||||
return PRELUDE + ES6_SHIM + DIRECTIVES + out + POSTLUDE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,10 @@ function isArray(obj) {
|
||||||
return Array.isArray(obj);
|
return Array.isArray(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTypeNameForDebugging (fn) {
|
||||||
|
return fn.name || 'Root';
|
||||||
|
}
|
||||||
|
|
||||||
var PromiseWrapper = {
|
var PromiseWrapper = {
|
||||||
resolve: function (reason) {
|
resolve: function (reason) {
|
||||||
return $q.when(reason);
|
return $q.when(reason);
|
||||||
|
@ -251,8 +255,8 @@ var StringWrapper = {
|
||||||
return s.replace(from, replace);
|
return s.replace(from, replace);
|
||||||
},
|
},
|
||||||
|
|
||||||
startsWith: function(s, start) {
|
startsWith: function(s, start) {
|
||||||
return s.startsWith(start);
|
return s.substr(0, start.length) === start;
|
||||||
},
|
},
|
||||||
|
|
||||||
replaceAllMapped: function(s, from, cb) {
|
replaceAllMapped: function(s, from, cb) {
|
||||||
|
@ -272,14 +276,18 @@ var StringWrapper = {
|
||||||
|
|
||||||
//TODO: implement?
|
//TODO: implement?
|
||||||
// I think it's too heavy to ask 1.x users to bring in Rx for the router...
|
// I think it's too heavy to ask 1.x users to bring in Rx for the router...
|
||||||
function EventEmitter() {
|
function EventEmitter() {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var BaseException = Error;
|
var BaseException = Error;
|
||||||
|
|
||||||
var ObservableWrapper = {
|
var ObservableWrapper = {
|
||||||
callNext: function(){}
|
callNext: function(ob, val) {
|
||||||
|
ob.fn(val);
|
||||||
|
},
|
||||||
|
|
||||||
|
subscribe: function(ob, fn) {
|
||||||
|
ob.fn = fn;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: https://github.com/angular/angular.js/blob/master/src/ng/browser.js#L227-L265
|
// TODO: https://github.com/angular/angular.js/blob/master/src/ng/browser.js#L227-L265
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
angular.module('ngComponentRouter').
|
||||||
|
value('$route', null). // can be overloaded with ngRouteShim
|
||||||
|
factory('$router', ['$q', '$location', '$$directiveIntrospector', '$browser', '$rootScope', '$injector', '$route', routerFactory]);
|
||||||
|
|
||||||
|
function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootScope, $injector) {
|
||||||
|
|
||||||
|
// When this file is processed, the line below is replaced with
|
||||||
|
// the contents of `../lib/facades.es5`.
|
||||||
|
//{{FACADES}}
|
||||||
|
|
||||||
|
var exports = {Injectable: function () {}};
|
||||||
|
var require = function () {return exports;};
|
||||||
|
|
||||||
|
// When this file is processed, the line below is replaced with
|
||||||
|
// the contents of the compiled TypeScript classes.
|
||||||
|
//{{SHARED_CODE}}
|
||||||
|
|
||||||
|
//TODO: this is a hack to replace the exiting implementation at run-time
|
||||||
|
exports.getCanActivateHook = function (directiveName) {
|
||||||
|
var factory = $$directiveIntrospector.getTypeByName(directiveName);
|
||||||
|
return factory && factory.$canActivate && function (next, prev) {
|
||||||
|
return $injector.invoke(factory.$canActivate, null, {
|
||||||
|
$nextInstruction: next,
|
||||||
|
$prevInstruction: prev
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// This hack removes assertions about the type of the "component"
|
||||||
|
// property in a route config
|
||||||
|
exports.assertComponentExists = function () {};
|
||||||
|
|
||||||
|
angular.stringifyInstruction = exports.stringifyInstruction;
|
||||||
|
|
||||||
|
var RouteRegistry = exports.RouteRegistry;
|
||||||
|
var RootRouter = exports.RootRouter;
|
||||||
|
|
||||||
|
var registry = new RouteRegistry();
|
||||||
|
var location = new Location();
|
||||||
|
|
||||||
|
$$directiveIntrospector(function (name, factory) {
|
||||||
|
if (angular.isArray(factory.$routeConfig)) {
|
||||||
|
factory.$routeConfig.forEach(function (config) {
|
||||||
|
registry.config(name, config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Because Angular 1 has no notion of a root component, we use an object with unique identity
|
||||||
|
// to represent this.
|
||||||
|
var ROOT_COMPONENT_OBJECT = new Object();
|
||||||
|
|
||||||
|
var router = new RootRouter(registry, location, ROOT_COMPONENT_OBJECT);
|
||||||
|
$rootScope.$watch(function () { return $location.path(); }, function (path) {
|
||||||
|
if (router.lastNavigationAttempt !== path) {
|
||||||
|
router.navigateByUrl(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.subscribe(function () {
|
||||||
|
$rootScope.$broadcast('$routeChangeSuccess', {});
|
||||||
|
});
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
|
@ -4,69 +4,63 @@
|
||||||
* A module for adding new a routing system Angular 1.
|
* A module for adding new a routing system Angular 1.
|
||||||
*/
|
*/
|
||||||
angular.module('ngComponentRouter', [])
|
angular.module('ngComponentRouter', [])
|
||||||
.factory('$componentMapper', $componentMapperFactory)
|
|
||||||
.directive('ngOutlet', ngOutletDirective)
|
.directive('ngOutlet', ngOutletDirective)
|
||||||
.directive('ngOutlet', ngOutletFillContentDirective)
|
.directive('ngOutlet', ngOutletFillContentDirective)
|
||||||
.directive('ngLink', ngLinkDirective)
|
.directive('ngLink', ngLinkDirective);
|
||||||
.directive('a', anchorLinkDirective); // TODO: make the anchor link feature configurable
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A module for inspecting controller constructors
|
* A module for inspecting controller constructors
|
||||||
*/
|
*/
|
||||||
angular.module('ng')
|
angular.module('ng')
|
||||||
.provider('$$controllerIntrospector', $$controllerIntrospectorProvider)
|
.provider('$$directiveIntrospector', $$directiveIntrospectorProvider)
|
||||||
.config(controllerProviderDecorator);
|
.config(compilerProviderDecorator);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* decorates with routing info
|
* decorates $compileProvider so that we have access to routing metadata
|
||||||
*/
|
*/
|
||||||
function controllerProviderDecorator($controllerProvider, $$controllerIntrospectorProvider) {
|
function compilerProviderDecorator($compileProvider, $$directiveIntrospectorProvider) {
|
||||||
var register = $controllerProvider.register;
|
var directive = $compileProvider.directive;
|
||||||
$controllerProvider.register = function (name, ctrl) {
|
$compileProvider.directive = function (name, factory) {
|
||||||
$$controllerIntrospectorProvider.register(name, ctrl);
|
$$directiveIntrospectorProvider.register(name, factory);
|
||||||
return register.apply(this, arguments);
|
return directive.apply(this, arguments);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: decorate $controller ?
|
|
||||||
/*
|
/*
|
||||||
* private service that holds route mappings for each controller
|
* private service that holds route mappings for each controller
|
||||||
*/
|
*/
|
||||||
function $$controllerIntrospectorProvider() {
|
function $$directiveIntrospectorProvider() {
|
||||||
var controllers = [];
|
var directiveBuffer = [];
|
||||||
var controllersByName = {};
|
var directiveFactoriesByName = {};
|
||||||
var onControllerRegistered = null;
|
var onDirectiveRegistered = null;
|
||||||
return {
|
return {
|
||||||
register: function (name, constructor) {
|
register: function (name, factory) {
|
||||||
if (angular.isArray(constructor)) {
|
if (angular.isArray(factory)) {
|
||||||
constructor = constructor[constructor.length - 1];
|
factory = factory[factory.length - 1];
|
||||||
}
|
}
|
||||||
controllersByName[name] = constructor;
|
directiveFactoriesByName[name] = factory;
|
||||||
constructor.$$controllerName = name;
|
if (onDirectiveRegistered) {
|
||||||
if (onControllerRegistered) {
|
onDirectiveRegistered(name, factory);
|
||||||
onControllerRegistered(name, constructor);
|
|
||||||
} else {
|
} else {
|
||||||
controllers.push({name: name, constructor: constructor});
|
directiveBuffer.push({name: name, factory: factory});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
$get: ['$componentMapper', function ($componentMapper) {
|
$get: function () {
|
||||||
var fn = function (newOnControllerRegistered) {
|
var fn = function (newOnControllerRegistered) {
|
||||||
onControllerRegistered = function (name, constructor) {
|
onDirectiveRegistered = newOnControllerRegistered;
|
||||||
name = $componentMapper.component(name);
|
while (directiveBuffer.length > 0) {
|
||||||
return newOnControllerRegistered(name, constructor);
|
var directive = directiveBuffer.pop();
|
||||||
};
|
onDirectiveRegistered(directive.name, directive.factory);
|
||||||
while (controllers.length > 0) {
|
|
||||||
var rule = controllers.pop();
|
|
||||||
onControllerRegistered(rule.name, rule.constructor);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn.getTypeByName = function (name) {
|
fn.getTypeByName = function (name) {
|
||||||
return controllersByName[name];
|
return directiveFactoriesByName[name];
|
||||||
};
|
};
|
||||||
|
|
||||||
return fn;
|
return fn;
|
||||||
}]
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +79,7 @@ function $$controllerIntrospectorProvider() {
|
||||||
*
|
*
|
||||||
* The value for the `ngOutlet` attribute is optional.
|
* The value for the `ngOutlet` attribute is optional.
|
||||||
*/
|
*/
|
||||||
function ngOutletDirective($animate, $q, $router, $componentMapper, $controller, $templateRequest) {
|
function ngOutletDirective($animate, $q, $router) {
|
||||||
var rootRouter = $router;
|
var rootRouter = $router;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -105,10 +99,12 @@ function ngOutletDirective($animate, $q, $router, $componentMapper, $controller,
|
||||||
myCtrl = ctrls[1],
|
myCtrl = ctrls[1],
|
||||||
router = (parentCtrl && parentCtrl.$$router) || rootRouter;
|
router = (parentCtrl && parentCtrl.$$router) || rootRouter;
|
||||||
|
|
||||||
|
myCtrl.$$currentComponent = null;
|
||||||
|
|
||||||
var childRouter,
|
var childRouter,
|
||||||
|
currentController,
|
||||||
currentInstruction,
|
currentInstruction,
|
||||||
currentScope,
|
currentScope,
|
||||||
currentController,
|
|
||||||
currentElement,
|
currentElement,
|
||||||
previousLeaveAnimation;
|
previousLeaveAnimation;
|
||||||
|
|
||||||
|
@ -136,8 +132,8 @@ function ngOutletDirective($animate, $q, $router, $componentMapper, $controller,
|
||||||
var next = $q.when(true);
|
var next = $q.when(true);
|
||||||
var previousInstruction = currentInstruction;
|
var previousInstruction = currentInstruction;
|
||||||
currentInstruction = instruction;
|
currentInstruction = instruction;
|
||||||
if (currentController.onReuse) {
|
if (currentController && currentController.$onReuse) {
|
||||||
next = $q.when(currentController.onReuse(currentInstruction, previousInstruction));
|
next = $q.when(currentController.$onReuse(currentInstruction, previousInstruction));
|
||||||
}
|
}
|
||||||
|
|
||||||
return next;
|
return next;
|
||||||
|
@ -147,8 +143,8 @@ function ngOutletDirective($animate, $q, $router, $componentMapper, $controller,
|
||||||
if (!currentInstruction ||
|
if (!currentInstruction ||
|
||||||
currentInstruction.componentType !== nextInstruction.componentType) {
|
currentInstruction.componentType !== nextInstruction.componentType) {
|
||||||
result = false;
|
result = false;
|
||||||
} else if (currentController.canReuse) {
|
} else if (currentController && currentController.$canReuse) {
|
||||||
result = currentController.canReuse(nextInstruction, currentInstruction);
|
result = currentController.$canReuse(nextInstruction, currentInstruction);
|
||||||
} else {
|
} else {
|
||||||
result = nextInstruction === currentInstruction ||
|
result = nextInstruction === currentInstruction ||
|
||||||
angular.equals(nextInstruction.params, currentInstruction.params);
|
angular.equals(nextInstruction.params, currentInstruction.params);
|
||||||
|
@ -156,60 +152,59 @@ function ngOutletDirective($animate, $q, $router, $componentMapper, $controller,
|
||||||
return $q.when(result);
|
return $q.when(result);
|
||||||
},
|
},
|
||||||
canDeactivate: function (instruction) {
|
canDeactivate: function (instruction) {
|
||||||
if (currentInstruction && currentController && currentController.canDeactivate) {
|
if (currentController && currentController.$canDeactivate) {
|
||||||
return $q.when(currentController.canDeactivate(instruction, currentInstruction));
|
return $q.when(currentController.$canDeactivate(instruction, currentInstruction));
|
||||||
}
|
}
|
||||||
return $q.when(true);
|
return $q.when(true);
|
||||||
},
|
},
|
||||||
deactivate: function (instruction) {
|
deactivate: function (instruction) {
|
||||||
if (currentController && currentController.onDeactivate) {
|
if (currentController && currentController.$onDeactivate) {
|
||||||
return $q.when(currentController.onDeactivate(instruction, currentInstruction));
|
return $q.when(currentController.$onDeactivate(instruction, currentInstruction));
|
||||||
}
|
}
|
||||||
return $q.when();
|
return $q.when();
|
||||||
},
|
},
|
||||||
activate: function (instruction) {
|
activate: function (instruction) {
|
||||||
var previousInstruction = currentInstruction;
|
var previousInstruction = currentInstruction;
|
||||||
currentInstruction = instruction;
|
currentInstruction = instruction;
|
||||||
childRouter = router.childRouter(instruction.componentType);
|
|
||||||
|
|
||||||
var controllerConstructor, componentName;
|
var componentName = myCtrl.$$componentName = instruction.componentType;
|
||||||
controllerConstructor = instruction.componentType;
|
|
||||||
componentName = $componentMapper.component(controllerConstructor.$$controllerName);
|
|
||||||
|
|
||||||
var componentTemplateUrl = $componentMapper.template(componentName);
|
if (typeof componentName != 'string') {
|
||||||
return $templateRequest(componentTemplateUrl).then(function (templateHtml) {
|
throw new Error('Component is not a string for ' + instruction.urlPath);
|
||||||
myCtrl.$$router = childRouter;
|
}
|
||||||
myCtrl.$$template = templateHtml;
|
|
||||||
}).then(function () {
|
|
||||||
var newScope = scope.$new();
|
|
||||||
var locals = {
|
|
||||||
$scope: newScope,
|
|
||||||
$router: childRouter,
|
|
||||||
$routeParams: (instruction.params || {})
|
|
||||||
};
|
|
||||||
|
|
||||||
// todo(shahata): controllerConstructor is not minify friendly
|
myCtrl.$$routeParams = instruction.params;
|
||||||
currentController = $controller(controllerConstructor, locals);
|
|
||||||
|
|
||||||
var clone = $transclude(newScope, function (clone) {
|
myCtrl.$$template = '<div ' + dashCase(componentName) + '></div>';
|
||||||
$animate.enter(clone, null, currentElement || $element);
|
|
||||||
cleanupLastView();
|
|
||||||
});
|
|
||||||
|
|
||||||
var controllerAs = $componentMapper.controllerAs(componentName) || componentName;
|
myCtrl.$$router = router.childRouter(instruction.componentType);
|
||||||
newScope[controllerAs] = currentController;
|
|
||||||
currentElement = clone;
|
|
||||||
currentScope = newScope;
|
|
||||||
|
|
||||||
if (currentController.onActivate) {
|
var newScope = scope.$new();
|
||||||
return currentController.onActivate(instruction, previousInstruction);
|
|
||||||
}
|
var clone = $transclude(newScope, function (clone) {
|
||||||
|
$animate.enter(clone, null, currentElement || $element);
|
||||||
|
cleanupLastView();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
currentElement = clone;
|
||||||
|
currentScope = newScope;
|
||||||
|
|
||||||
|
// TODO: prefer the other directive retrieving the controller
|
||||||
|
// by debug mode
|
||||||
|
currentController = currentElement.children().eq(0).controller(componentName);
|
||||||
|
|
||||||
|
if (currentController && currentController.$onActivate) {
|
||||||
|
return currentController.$onActivate(instruction, previousInstruction);
|
||||||
|
}
|
||||||
|
return $q.when();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This directive is responsible for compiling the contents of ng-outlet
|
||||||
|
*/
|
||||||
function ngOutletFillContentDirective($compile) {
|
function ngOutletFillContentDirective($compile) {
|
||||||
return {
|
return {
|
||||||
restrict: 'EA',
|
restrict: 'EA',
|
||||||
|
@ -220,6 +215,15 @@ function ngOutletFillContentDirective($compile) {
|
||||||
$element.html(template);
|
$element.html(template);
|
||||||
var link = $compile($element.contents());
|
var link = $compile($element.contents());
|
||||||
link(scope);
|
link(scope);
|
||||||
|
|
||||||
|
// TODO: move to primary directive
|
||||||
|
var componentInstance = scope[ctrl.$$componentName];
|
||||||
|
if (componentInstance) {
|
||||||
|
ctrl.$$currentComponent = componentInstance;
|
||||||
|
|
||||||
|
componentInstance.$router = ctrl.$$router;
|
||||||
|
componentInstance.$routeParams = ctrl.$$routeParams;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -249,7 +253,7 @@ function ngOutletFillContentDirective($compile) {
|
||||||
* </div>
|
* </div>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
function ngLinkDirective($router, $location, $parse) {
|
function ngLinkDirective($router, $parse) {
|
||||||
var rootRouter = $router;
|
var rootRouter = $router;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -264,10 +268,12 @@ function ngLinkDirective($router, $location, $parse) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var instruction = null;
|
||||||
var link = attrs.ngLink || '';
|
var link = attrs.ngLink || '';
|
||||||
|
|
||||||
function getLink(params) {
|
function getLink(params) {
|
||||||
return './' + angular.stringifyInstruction(router.generate(params));
|
instruction = router.generate(params);
|
||||||
|
return './' + angular.stringifyInstruction(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
var routeParamsGetter = $parse(link);
|
var routeParamsGetter = $parse(link);
|
||||||
|
@ -282,128 +288,16 @@ function ngLinkDirective($router, $location, $parse) {
|
||||||
elt.attr('href', getLink(params));
|
elt.attr('href', getLink(params));
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
elt.on('click', function (event) {
|
||||||
function anchorLinkDirective($router) {
|
if (event.which !== 1 || !instruction) {
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
link: function (scope, element) {
|
|
||||||
// If the linked element is not an anchor tag anymore, do nothing
|
|
||||||
if (element[0].nodeName.toLowerCase() !== 'a') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
$router.navigateByInstruction(instruction);
|
||||||
var hrefAttrName = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
|
event.preventDefault();
|
||||||
'xlink:href' : 'href';
|
});
|
||||||
|
}
|
||||||
element.on('click', function (event) {
|
|
||||||
if (event.which !== 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var href = element.attr(hrefAttrName);
|
|
||||||
if (href && $router.recognize(href)) {
|
|
||||||
$router.navigateByUrl(href);
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name $componentMapperFactory
|
|
||||||
* @description
|
|
||||||
*
|
|
||||||
* This lets you configure conventions for what controllers are named and where to load templates from.
|
|
||||||
*
|
|
||||||
* The default behavior is to dasherize and serve from `./components`. A component called `myWidget`
|
|
||||||
* uses a controller named `MyWidgetController` and a template loaded from `./components/my-widget/my-widget.html`.
|
|
||||||
*
|
|
||||||
* A component is:
|
|
||||||
* - a controller
|
|
||||||
* - a template
|
|
||||||
* - an optional router
|
|
||||||
*
|
|
||||||
* This service makes it easy to group all of them into a single concept.
|
|
||||||
*/
|
|
||||||
function $componentMapperFactory() {
|
|
||||||
|
|
||||||
var DEFAULT_SUFFIX = 'Controller';
|
|
||||||
|
|
||||||
var componentToCtrl = function componentToCtrlDefault(name) {
|
|
||||||
return name[0].toUpperCase() + name.substr(1) + DEFAULT_SUFFIX;
|
|
||||||
};
|
|
||||||
|
|
||||||
var componentToTemplate = function componentToTemplateDefault(name) {
|
|
||||||
var dashName = dashCase(name);
|
|
||||||
return './components/' + dashName + '/' + dashName + '.html';
|
|
||||||
};
|
|
||||||
|
|
||||||
var ctrlToComponent = function ctrlToComponentDefault(name) {
|
|
||||||
return name[0].toLowerCase() + name.substr(1, name.length - DEFAULT_SUFFIX.length - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
var componentToControllerAs = function componentToControllerAsDefault(name) {
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
controllerName: function (name) {
|
|
||||||
return componentToCtrl(name);
|
|
||||||
},
|
|
||||||
|
|
||||||
controllerAs: function (name) {
|
|
||||||
return componentToControllerAs(name);
|
|
||||||
},
|
|
||||||
|
|
||||||
template: function (name) {
|
|
||||||
return componentToTemplate(name);
|
|
||||||
},
|
|
||||||
|
|
||||||
component: function (name) {
|
|
||||||
return ctrlToComponent(name);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name $componentMapper#setCtrlNameMapping
|
|
||||||
* @description takes a function for mapping component names to component controller names
|
|
||||||
*/
|
|
||||||
setCtrlNameMapping: function (newFn) {
|
|
||||||
componentToCtrl = newFn;
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name $componentMapper#setCtrlAsMapping
|
|
||||||
* @description takes a function for mapping component names to controllerAs name in the template
|
|
||||||
*/
|
|
||||||
setCtrlAsMapping: function (newFn) {
|
|
||||||
componentToControllerAs = newFn;
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name $componentMapper#setComponentFromCtrlMapping
|
|
||||||
* @description takes a function for mapping component controller names to component names
|
|
||||||
*/
|
|
||||||
setComponentFromCtrlMapping: function (newFn) {
|
|
||||||
ctrlToComponent = newFn;
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name $componentMapper#setTemplateMapping
|
|
||||||
* @description takes a function for mapping component names to component template URLs
|
|
||||||
*/
|
|
||||||
setTemplateMapping: function (newFn) {
|
|
||||||
componentToTemplate = newFn;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe('$componentMapper', function () {
|
|
||||||
var elt,
|
|
||||||
$compile,
|
|
||||||
$rootScope,
|
|
||||||
$router,
|
|
||||||
$templateCache;
|
|
||||||
|
|
||||||
function Ctrl() {
|
|
||||||
this.message = 'howdy';
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
module('ng');
|
|
||||||
module('ngComponentRouter');
|
|
||||||
module(function ($controllerProvider) {
|
|
||||||
$controllerProvider.register('myComponentController', Ctrl);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should convert a component name to a controller name', inject(function ($componentMapper) {
|
|
||||||
expect($componentMapper.controllerName('foo')).toBe('FooController');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should convert a controller name to a component name', inject(function ($componentMapper) {
|
|
||||||
expect($componentMapper.component('FooController')).toBe('foo');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should convert a component name to a template URL', inject(function ($componentMapper) {
|
|
||||||
expect($componentMapper.template('foo')).toBe('./components/foo/foo.html');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should work with a controller constructor fn and a template url', inject(function ($componentMapper) {
|
|
||||||
var routes = {};
|
|
||||||
$componentMapper.setCtrlNameMapping(function (name) {
|
|
||||||
return routes[name].controller;
|
|
||||||
});
|
|
||||||
$componentMapper.setTemplateMapping(function (name) {
|
|
||||||
return routes[name].templateUrl;
|
|
||||||
});
|
|
||||||
$componentMapper.setCtrlAsMapping(function (name) {
|
|
||||||
return 'ctrl';
|
|
||||||
});
|
|
||||||
|
|
||||||
routes.myComponent = {
|
|
||||||
controller: Ctrl,
|
|
||||||
templateUrl: '/foo'
|
|
||||||
};
|
|
||||||
|
|
||||||
inject(function(_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
|
||||||
$compile = _$compile_;
|
|
||||||
$rootScope = _$rootScope_;
|
|
||||||
$router = _$router_;
|
|
||||||
$templateCache = _$templateCache_;
|
|
||||||
});
|
|
||||||
|
|
||||||
$templateCache.put('/foo', [200, '{{ctrl.message}}', {}]);
|
|
||||||
|
|
||||||
compile('<ng-outlet></ng-outlet>');
|
|
||||||
|
|
||||||
$router.config([
|
|
||||||
{ path: '/', component: Ctrl }
|
|
||||||
]);
|
|
||||||
|
|
||||||
$router.navigateByUrl('/');
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
expect(elt.text()).toBe('howdy');
|
|
||||||
}));
|
|
||||||
|
|
||||||
function compile(template) {
|
|
||||||
elt = $compile('<div>' + template + '</div>')($rootScope);
|
|
||||||
$rootScope.$digest();
|
|
||||||
return elt;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,38 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe('$$controllerIntrospector', function () {
|
|
||||||
|
|
||||||
var $controllerProvider;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
module('ng');
|
|
||||||
module('ngComponentRouter');
|
|
||||||
module(function(_$controllerProvider_) {
|
|
||||||
$controllerProvider = _$controllerProvider_;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should call the introspector function whenever a controller is registered', inject(function ($$controllerIntrospector) {
|
|
||||||
var spy = jasmine.createSpy();
|
|
||||||
$$controllerIntrospector(spy);
|
|
||||||
function Ctrl(){}
|
|
||||||
$controllerProvider.register('SomeController', Ctrl);
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('some', Ctrl);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call the introspector function whenever a controller is registered with array annotations', inject(function ($$controllerIntrospector) {
|
|
||||||
var spy = jasmine.createSpy();
|
|
||||||
$$controllerIntrospector(spy);
|
|
||||||
function Ctrl(foo){}
|
|
||||||
$controllerProvider.register('SomeController', ['foo', Ctrl]);
|
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith('some', Ctrl);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should retrieve a constructor', inject(function ($$controllerIntrospector) {
|
|
||||||
function Ctrl(foo){}
|
|
||||||
$controllerProvider.register('SomeController', ['foo', Ctrl]);
|
|
||||||
expect($$controllerIntrospector.getTypeByName('SomeController')).toBe(Ctrl);
|
|
||||||
}));
|
|
||||||
});
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('$$directiveIntrospector', function () {
|
||||||
|
|
||||||
|
var $compileProvider;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
module('ng');
|
||||||
|
module('ngComponentRouter');
|
||||||
|
module(function(_$compileProvider_) {
|
||||||
|
$compileProvider = _$compileProvider_;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call the introspector function whenever a directive factory is registered', inject(function ($$directiveIntrospector) {
|
||||||
|
var spy = jasmine.createSpy();
|
||||||
|
$$directiveIntrospector(spy);
|
||||||
|
function myDir(){}
|
||||||
|
$compileProvider.directive('myDir', myDir);
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith('myDir', myDir);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call the introspector function whenever a directive factory is registered with array annotations', inject(function ($$directiveIntrospector) {
|
||||||
|
var spy = jasmine.createSpy();
|
||||||
|
$$directiveIntrospector(spy);
|
||||||
|
function myDir(){}
|
||||||
|
$compileProvider.directive('myDir', ['foo', myDir]);
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith('myDir', myDir);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should retrieve a factory based on directive name', inject(function ($$directiveIntrospector) {
|
||||||
|
function myDir(){}
|
||||||
|
$compileProvider.directive('myDir', ['foo', myDir]);
|
||||||
|
expect($$directiveIntrospector.getTypeByName('myDir')).toBe(myDir);
|
||||||
|
}));
|
||||||
|
});
|
|
@ -2,35 +2,31 @@
|
||||||
|
|
||||||
describe('ngOutlet animations', function () {
|
describe('ngOutlet animations', function () {
|
||||||
var elt,
|
var elt,
|
||||||
$animate,
|
$animate,
|
||||||
$compile,
|
$compile,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$router,
|
$router,
|
||||||
$templateCache,
|
$compileProvider;
|
||||||
$controllerProvider;
|
|
||||||
|
|
||||||
function UserController($routeParams) {
|
|
||||||
this.name = $routeParams.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
module('ng');
|
||||||
module('ngAnimate');
|
module('ngAnimate');
|
||||||
module('ngAnimateMock');
|
module('ngAnimateMock');
|
||||||
module('ngComponentRouter');
|
module('ngComponentRouter');
|
||||||
module(function (_$controllerProvider_) {
|
module(function (_$compileProvider_) {
|
||||||
$controllerProvider = _$controllerProvider_;
|
$compileProvider = _$compileProvider_;
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
inject(function (_$animate_, _$compile_, _$rootScope_, _$router_) {
|
||||||
$animate = _$animate_;
|
$animate = _$animate_;
|
||||||
$compile = _$compile_;
|
$compile = _$compile_;
|
||||||
$rootScope = _$rootScope_;
|
$rootScope = _$rootScope_;
|
||||||
$router = _$router_;
|
$router = _$router_;
|
||||||
$templateCache = _$templateCache_;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
put('user', '<div>hello {{user.name}}</div>');
|
registerComponent('userCmp', {
|
||||||
$controllerProvider.register('UserController', UserController);
|
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
@ -43,7 +39,7 @@ describe('ngOutlet animations', function () {
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: UserController }
|
{ path: '/user/:name', component: 'userCmp' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$router.navigateByUrl('/user/brian');
|
$router.navigateByUrl('/user/brian');
|
||||||
|
@ -70,8 +66,32 @@ describe('ngOutlet animations', function () {
|
||||||
expect(item.element.text()).toBe('hello brian');
|
expect(item.element.text()).toBe('hello brian');
|
||||||
});
|
});
|
||||||
|
|
||||||
function put(name, template) {
|
|
||||||
$templateCache.put(componentTemplatePath(name), [200, template, {}]);
|
function registerComponent(name, options) {
|
||||||
|
var controller = options.controller || function () {};
|
||||||
|
|
||||||
|
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||||
|
if (options[hookName]) {
|
||||||
|
controller.prototype[hookName] = options[hookName];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function factory() {
|
||||||
|
return {
|
||||||
|
template: options.template || '',
|
||||||
|
controllerAs: name,
|
||||||
|
controller: controller
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.$canActivate) {
|
||||||
|
factory.$canActivate = options.$canActivate;
|
||||||
|
}
|
||||||
|
if (options.$routeConfig) {
|
||||||
|
factory.$routeConfig = options.$routeConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
$compileProvider.directive(name, factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(template) {
|
function compile(template) {
|
|
@ -1,53 +1,45 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('ngOutlet', function () {
|
describe('Navigation lifecycle', function () {
|
||||||
|
|
||||||
var elt,
|
var elt,
|
||||||
$compile,
|
$compile,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$router,
|
$router,
|
||||||
$templateCache,
|
$compileProvider;
|
||||||
$controllerProvider,
|
|
||||||
$componentMapperProvider;
|
|
||||||
|
|
||||||
var OneController, TwoController, UserController;
|
|
||||||
|
|
||||||
function instructionFor(componentType) {
|
|
||||||
return jasmine.objectContaining({componentType: componentType});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
module('ng');
|
module('ng');
|
||||||
module('ngComponentRouter');
|
module('ngComponentRouter');
|
||||||
module(function (_$controllerProvider_, _$componentMapperProvider_) {
|
module(function (_$compileProvider_) {
|
||||||
$controllerProvider = _$controllerProvider_;
|
$compileProvider = _$compileProvider_;
|
||||||
$componentMapperProvider = _$componentMapperProvider_;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function (_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||||
$compile = _$compile_;
|
$compile = _$compile_;
|
||||||
$rootScope = _$rootScope_;
|
$rootScope = _$rootScope_;
|
||||||
$router = _$router_;
|
$router = _$router_;
|
||||||
$templateCache = _$templateCache_;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
UserController = registerComponent('user', '<div>hello {{user.name}}</div>', function ($routeParams) {
|
registerComponent('oneCmp', {
|
||||||
this.name = $routeParams.name;
|
template: '<div>{{oneCmp.number}}</div>',
|
||||||
|
controller: function () {this.number = 'one'}
|
||||||
|
});
|
||||||
|
registerComponent('twoCmp', {
|
||||||
|
template: '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>',
|
||||||
|
controller: function () {this.number = 'two'}
|
||||||
});
|
});
|
||||||
OneController = registerComponent('one', '<div>{{one.number}}</div>', boringController('number', 'one'));
|
|
||||||
TwoController = registerComponent('two', '<div>{{two.number}}</div>', boringController('number', 'two'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should run the activate hook of controllers', function () {
|
it('should run the activate hook of controllers', function () {
|
||||||
var spy = jasmine.createSpy('activate');
|
var spy = jasmine.createSpy('activate');
|
||||||
var activate = registerComponent('activate', '', {
|
registerComponent('activateCmp', {
|
||||||
onActivate: spy
|
template: '<p>hello</p>',
|
||||||
|
$onActivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate }
|
{ path: '/a', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||||
|
|
||||||
|
@ -60,31 +52,32 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should pass instruction into the activate hook of a controller', function () {
|
it('should pass instruction into the activate hook of a controller', function () {
|
||||||
var spy = jasmine.createSpy('activate');
|
var spy = jasmine.createSpy('activate');
|
||||||
var UserController = registerComponent('user', '', {
|
registerComponent('userCmp', {
|
||||||
onActivate: spy
|
$onActivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: UserController }
|
{ path: '/user/:name', component: 'userCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
$router.navigateByUrl('/user/brian');
|
$router.navigateByUrl('/user/brian');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalledWith(instructionFor(UserController), undefined);
|
expect(spy).toHaveBeenCalledWith(instructionFor('userCmp'), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should pass previous instruction into the activate hook of a controller', function () {
|
it('should pass previous instruction into the activate hook of a controller', function () {
|
||||||
var spy = jasmine.createSpy('activate');
|
var spy = jasmine.createSpy('activate');
|
||||||
var activate = registerComponent('activate', '', {
|
var activate = registerComponent('activateCmp', {
|
||||||
onActivate: spy
|
template: 'hi',
|
||||||
|
$onActivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: OneController },
|
{ path: '/user/:name', component: 'oneCmp' },
|
||||||
{ path: '/post/:id', component: activate }
|
{ path: '/post/:id', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -92,19 +85,21 @@ describe('ngOutlet', function () {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
$router.navigateByUrl('/post/123');
|
$router.navigateByUrl('/post/123');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(spy).toHaveBeenCalledWith(instructionFor(activate),
|
expect(spy).toHaveBeenCalledWith(instructionFor('activateCmp'),
|
||||||
instructionFor(OneController));
|
instructionFor('oneCmp'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject $scope into the controller constructor', function () {
|
it('should inject $scope into the controller constructor', function () {
|
||||||
|
|
||||||
var injectedScope;
|
var injectedScope;
|
||||||
var UserController = registerComponent('user', '', function ($scope) {
|
registerComponent('userCmp', {
|
||||||
injectedScope = $scope;
|
template: '',
|
||||||
|
controller: function ($scope) {
|
||||||
|
injectedScope = $scope;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user', component: UserController }
|
{ path: '/user', component: 'userCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -117,13 +112,13 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should run the deactivate hook of controllers', function () {
|
it('should run the deactivate hook of controllers', function () {
|
||||||
var spy = jasmine.createSpy('deactivate');
|
var spy = jasmine.createSpy('deactivate');
|
||||||
var deactivate = registerComponent('deactivate', '', {
|
registerComponent('deactivateCmp', {
|
||||||
onDeactivate: spy
|
$onDeactivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: deactivate },
|
{ path: '/a', component: 'deactivateCmp' },
|
||||||
{ path: '/b', component: OneController }
|
{ path: '/b', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -137,13 +132,13 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should pass instructions into the deactivate hook of controllers', function () {
|
it('should pass instructions into the deactivate hook of controllers', function () {
|
||||||
var spy = jasmine.createSpy('deactivate');
|
var spy = jasmine.createSpy('deactivate');
|
||||||
var deactivate = registerComponent('deactivate', '', {
|
registerComponent('deactivateCmp', {
|
||||||
onDeactivate: spy
|
$onDeactivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: deactivate },
|
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||||
{ path: '/post/:id', component: OneController }
|
{ path: '/post/:id', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -151,29 +146,29 @@ describe('ngOutlet', function () {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
$router.navigateByUrl('/post/123');
|
$router.navigateByUrl('/post/123');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(spy).toHaveBeenCalledWith(instructionFor(OneController),
|
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||||
instructionFor(deactivate));
|
instructionFor('deactivateCmp'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should run the deactivate hook before the activate hook', function () {
|
it('should run the deactivate hook before the activate hook', function () {
|
||||||
var log = [];
|
var log = [];
|
||||||
|
|
||||||
var activate = registerComponent('activate', '', {
|
registerComponent('activateCmp', {
|
||||||
onActivate: function () {
|
$onActivate: function () {
|
||||||
log.push('activate');
|
log.push('activate');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var deactivate = registerComponent('deactivate', '', {
|
registerComponent('deactivateCmp', {
|
||||||
onDeactivate: function () {
|
$onDeactivate: function () {
|
||||||
log.push('deactivate');
|
log.push('deactivate');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: deactivate },
|
{ path: '/a', component: 'deactivateCmp' },
|
||||||
{ path: '/b', component: activate }
|
{ path: '/b', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -185,25 +180,32 @@ describe('ngOutlet', function () {
|
||||||
expect(log).toEqual(['deactivate', 'activate']);
|
expect(log).toEqual(['deactivate', 'activate']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reuse a component when the canReuse hook returns true', function () {
|
it('should reuse a component when the canReuse hook returns true', function () {
|
||||||
var log = [];
|
var log = [];
|
||||||
var cmpInstanceCount = 0;
|
var cmpInstanceCount = 0;
|
||||||
|
|
||||||
function ReuseCmp() {
|
function ReuseCmp() {
|
||||||
cmpInstanceCount++;
|
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);
|
registerComponent('reuseCmp', {
|
||||||
|
template: 'reuse {<ng-outlet></ng-outlet>}',
|
||||||
|
$routeConfig: [
|
||||||
|
{path: '/a', component: 'oneCmp'},
|
||||||
|
{path: '/b', component: 'twoCmp'}
|
||||||
|
],
|
||||||
|
controller: ReuseCmp,
|
||||||
|
$canReuse: function () {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
$onReuse: function (next, prev) {
|
||||||
|
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/on-reuse/:number/...', component: ReuseCmp }
|
{ path: '/on-reuse/:number/...', component: 'reuseCmp' },
|
||||||
|
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -227,18 +229,25 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
function NeverReuseCmp() {
|
function NeverReuseCmp() {
|
||||||
cmpInstanceCount++;
|
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('reuseCmp', {
|
||||||
registerComponent('reuse', 'reuse {<ng-outlet></ng-outlet>}', NeverReuseCmp);
|
template: 'reuse {<ng-outlet></ng-outlet>}',
|
||||||
|
$routeConfig: [
|
||||||
|
{path: '/a', component: 'oneCmp'},
|
||||||
|
{path: '/b', component: 'twoCmp'}
|
||||||
|
],
|
||||||
|
controller: NeverReuseCmp,
|
||||||
|
$canReuse: function () {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
$onReuse: function (next, prev) {
|
||||||
|
log.push('reuse: ' + prev.urlPath + ' -> ' + next.urlPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/never-reuse/:number/...', component: NeverReuseCmp }
|
{ path: '/never-reuse/:number/...', component: 'reuseCmp' },
|
||||||
|
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -256,17 +265,17 @@ describe('ngOutlet', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: need to solve getting ahold of canActivate hook
|
||||||
it('should not activate a component when canActivate returns false', function () {
|
it('should not activate a component when canActivate returns false', function () {
|
||||||
|
var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(false);
|
||||||
var spy = jasmine.createSpy('activate');
|
var spy = jasmine.createSpy('activate');
|
||||||
var activate = registerComponent('activate', '', {
|
registerComponent('activateCmp', {
|
||||||
canActivate: function () {
|
$canActivate: canActivateSpy,
|
||||||
return false;
|
$onActivate: spy
|
||||||
},
|
|
||||||
onActivate: spy
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate }
|
{ path: '/a', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -279,38 +288,40 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
|
|
||||||
it('should activate a component when canActivate returns true', function () {
|
it('should activate a component when canActivate returns true', function () {
|
||||||
var spy = jasmine.createSpy('activate');
|
var activateSpy = jasmine.createSpy('activate');
|
||||||
var activate = registerComponent('activate', 'hi', {
|
var canActivateSpy = jasmine.createSpy('canActivate').and.returnValue(true);
|
||||||
canActivate: function () {
|
registerComponent('activateCmp', {
|
||||||
return true;
|
template: 'hi',
|
||||||
},
|
$canActivate: canActivateSpy,
|
||||||
onActivate: spy
|
$onActivate: activateSpy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate }
|
{ path: '/a', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
$router.navigateByUrl('/a');
|
$router.navigateByUrl('/a');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(canActivateSpy).toHaveBeenCalled();
|
||||||
|
expect(activateSpy).toHaveBeenCalled();
|
||||||
expect(elt.text()).toBe('hi');
|
expect(elt.text()).toBe('hi');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should activate a component when canActivate returns a resolved promise', inject(function ($q) {
|
it('should activate a component when canActivate returns a resolved promise', inject(function ($q) {
|
||||||
var spy = jasmine.createSpy('activate');
|
var spy = jasmine.createSpy('activate');
|
||||||
var activate = registerComponent('activate', 'hi', {
|
registerComponent('activateCmp', {
|
||||||
canActivate: function () {
|
template: 'hi',
|
||||||
|
$canActivate: function () {
|
||||||
return $q.when(true);
|
return $q.when(true);
|
||||||
},
|
},
|
||||||
onActivate: spy
|
$onActivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate }
|
{ path: '/a', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -324,33 +335,38 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should inject into the canActivate hook of controllers', inject(function ($http) {
|
it('should inject into the canActivate hook of controllers', inject(function ($http) {
|
||||||
var spy = jasmine.createSpy('canActivate').and.returnValue(true);
|
var spy = jasmine.createSpy('canActivate').and.returnValue(true);
|
||||||
var activate = registerComponent('activate', '', {
|
registerComponent('activateCmp', {
|
||||||
canActivate: spy
|
$canActivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
spy.$inject = ['$routeParams', '$http'];
|
spy.$inject = ['$nextInstruction', '$http'];
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: activate }
|
{ path: '/user/:name', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
$router.navigateByUrl('/user/brian');
|
$router.navigateByUrl('/user/brian');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(spy).toHaveBeenCalledWith({name: 'brian'}, $http);
|
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
var args = spy.calls.mostRecent().args;
|
||||||
|
expect(args[0].params).toEqual({name: 'brian'});
|
||||||
|
expect(args[1]).toBe($http);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should not navigate when canDeactivate returns false', function () {
|
it('should not navigate when canDeactivate returns false', function () {
|
||||||
var activate = registerComponent('activate', 'hi', {
|
registerComponent('activateCmp', {
|
||||||
canDeactivate: function () {
|
template: 'hi',
|
||||||
|
$canDeactivate: function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate },
|
{ path: '/a', component: 'activateCmp' },
|
||||||
{ path: '/b', component: OneController }
|
{ path: '/b', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -365,15 +381,16 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
|
|
||||||
it('should navigate when canDeactivate returns true', function () {
|
it('should navigate when canDeactivate returns true', function () {
|
||||||
var activate = registerComponent('activate', 'hi', {
|
registerComponent('activateCmp', {
|
||||||
canDeactivate: function () {
|
template: 'hi',
|
||||||
|
$canDeactivate: function () {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate },
|
{ path: '/a', component: 'activateCmp' },
|
||||||
{ path: '/b', component: OneController }
|
{ path: '/b', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -389,15 +406,16 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should activate a component when canActivate returns true', function () {
|
it('should activate a component when canActivate returns true', function () {
|
||||||
var spy = jasmine.createSpy('activate');
|
var spy = jasmine.createSpy('activate');
|
||||||
var activate = registerComponent('activate', 'hi', {
|
registerComponent('activateCmp', {
|
||||||
canActivate: function () {
|
template: 'hi',
|
||||||
|
$canActivate: function () {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
onActivate: spy
|
$onActivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: activate }
|
{ path: '/a', component: 'activateCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -411,13 +429,13 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should pass instructions into the canDeactivate hook of controllers', function () {
|
it('should pass instructions into the canDeactivate hook of controllers', function () {
|
||||||
var spy = jasmine.createSpy('canDeactivate').and.returnValue(true);
|
var spy = jasmine.createSpy('canDeactivate').and.returnValue(true);
|
||||||
var deactivate = registerComponent('deactivate', '', {
|
registerComponent('deactivateCmp', {
|
||||||
canDeactivate: spy
|
$canDeactivate: spy
|
||||||
});
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: deactivate },
|
{ path: '/user/:name', component: 'deactivateCmp' },
|
||||||
{ path: '/post/:id', component: OneController }
|
{ path: '/post/:id', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -425,43 +443,36 @@ describe('ngOutlet', function () {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
$router.navigateByUrl('/post/123');
|
$router.navigateByUrl('/post/123');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(spy).toHaveBeenCalledWith(instructionFor(OneController),
|
expect(spy).toHaveBeenCalledWith(instructionFor('oneCmp'),
|
||||||
instructionFor(deactivate));
|
instructionFor('deactivateCmp'));
|
||||||
});
|
});
|
||||||
|
|
||||||
function registerComponent(name, template, config) {
|
|
||||||
var Ctrl;
|
function registerComponent(name, options) {
|
||||||
if (!template) {
|
var controller = options.controller || function () {};
|
||||||
template = '';
|
|
||||||
}
|
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||||
if (!config) {
|
if (options[hookName]) {
|
||||||
Ctrl = function () {};
|
controller.prototype[hookName] = options[hookName];
|
||||||
} else if (angular.isArray(config)) {
|
|
||||||
Ctrl = function () {};
|
|
||||||
Ctrl.annotations = [new angular.annotations.RouteConfig(config)];
|
|
||||||
} else if (typeof config === 'function') {
|
|
||||||
Ctrl = config;
|
|
||||||
} else {
|
|
||||||
Ctrl = function () {};
|
|
||||||
if (config.canActivate) {
|
|
||||||
Ctrl.$canActivate = config.canActivate;
|
|
||||||
delete config.canActivate;
|
|
||||||
}
|
}
|
||||||
Ctrl.prototype = config;
|
});
|
||||||
|
|
||||||
|
function factory() {
|
||||||
|
return {
|
||||||
|
template: options.template || '',
|
||||||
|
controllerAs: name,
|
||||||
|
controller: controller
|
||||||
|
};
|
||||||
}
|
}
|
||||||
$controllerProvider.register(componentControllerName(name), Ctrl);
|
|
||||||
put(name, template);
|
|
||||||
return Ctrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function boringController(model, value) {
|
if (options.$canActivate) {
|
||||||
return function () {
|
factory.$canActivate = options.$canActivate;
|
||||||
this[model] = value;
|
}
|
||||||
};
|
if (options.$routeConfig) {
|
||||||
}
|
factory.$routeConfig = options.$routeConfig;
|
||||||
|
}
|
||||||
|
|
||||||
function put(name, template) {
|
$compileProvider.directive(name, factory);
|
||||||
$templateCache.put(componentTemplatePath(name), [200, template, {}]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(template) {
|
function compile(template) {
|
||||||
|
@ -469,4 +480,8 @@ describe('ngOutlet', function () {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
return elt;
|
return elt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function instructionFor(componentType) {
|
||||||
|
return jasmine.objectContaining({componentType: componentType});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,41 +6,39 @@ describe('navigation', function () {
|
||||||
$compile,
|
$compile,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$router,
|
$router,
|
||||||
$templateCache,
|
$compileProvider;
|
||||||
$controllerProvider,
|
|
||||||
$componentMapperProvider;
|
|
||||||
|
|
||||||
var OneController, TwoController, UserController;
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
module('ng');
|
module('ng');
|
||||||
module('ngComponentRouter');
|
module('ngComponentRouter');
|
||||||
module(function (_$controllerProvider_, _$componentMapperProvider_) {
|
module(function (_$compileProvider_) {
|
||||||
$controllerProvider = _$controllerProvider_;
|
$compileProvider = _$compileProvider_;
|
||||||
$componentMapperProvider = _$componentMapperProvider_;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function (_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||||
$compile = _$compile_;
|
$compile = _$compile_;
|
||||||
$rootScope = _$rootScope_;
|
$rootScope = _$rootScope_;
|
||||||
$router = _$router_;
|
$router = _$router_;
|
||||||
$templateCache = _$templateCache_;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
UserController = registerComponent('user', '<div>hello {{user.name}}</div>', function ($routeParams) {
|
registerComponent('userCmp', {
|
||||||
this.name = $routeParams.name;
|
template: '<div>hello {{userCmp.$routeParams.name}}</div>'
|
||||||
|
});
|
||||||
|
registerComponent('oneCmp', {
|
||||||
|
template: '<div>{{oneCmp.number}}</div>',
|
||||||
|
controller: function () {this.number = 'one'}
|
||||||
|
});
|
||||||
|
registerComponent('twoCmp', {
|
||||||
|
template: '<div>{{twoCmp.number}}</div>',
|
||||||
|
controller: function () {this.number = 'two'}
|
||||||
});
|
});
|
||||||
OneController = registerComponent('one', '<div>{{one.number}}</div>', boringController('number', 'one'));
|
|
||||||
TwoController = registerComponent('two', '<div>{{two.number}}</div>', boringController('number', 'two'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should work in a simple case', function () {
|
it('should work in a simple case', function () {
|
||||||
compile('<ng-outlet></ng-outlet>');
|
compile('<ng-outlet></ng-outlet>');
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/', component: OneController }
|
{ path: '/', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$router.navigateByUrl('/');
|
$router.navigateByUrl('/');
|
||||||
|
@ -49,26 +47,9 @@ describe('navigation', function () {
|
||||||
expect(elt.text()).toBe('one');
|
expect(elt.text()).toBe('one');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// See https://github.com/angular/router/issues/105
|
|
||||||
xit('should warn when instantiating a component with no controller', function () {
|
|
||||||
put('noController', '<div>{{ 2 + 2 }}</div>');
|
|
||||||
$router.config([
|
|
||||||
{ path: '/', component: 'noController' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
spyOn(console, 'warn');
|
|
||||||
compile('<ng-outlet></ng-outlet>');
|
|
||||||
$router.navigateByUrl('/');
|
|
||||||
|
|
||||||
expect(console.warn).toHaveBeenCalledWith('Could not find controller for', 'NoControllerController');
|
|
||||||
expect(elt.text()).toBe('4');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should navigate between components with different parameters', function () {
|
it('should navigate between components with different parameters', function () {
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user/:name', component: UserController }
|
{ path: '/user/:name', component: 'userCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<ng-outlet></ng-outlet>');
|
compile('<ng-outlet></ng-outlet>');
|
||||||
|
|
||||||
|
@ -82,42 +63,46 @@ describe('navigation', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should not reactivate a parent when navigating between child components with different parameters', function () {
|
it('should reuse a parent when navigating between child components with different parameters', function () {
|
||||||
var spy = jasmine.createSpy('onActivate');
|
var instanceCount = 0;
|
||||||
function ParentController() {}
|
function ParentController() {
|
||||||
ParentController.$routeConfig = [
|
instanceCount += 1;
|
||||||
{ path: '/user/:name', component: UserController }
|
}
|
||||||
];
|
registerComponent('parentCmp', {
|
||||||
ParentController.prototype.onActivate = spy;
|
template: 'parent { <ng-outlet></ng-outlet> }',
|
||||||
|
$routeConfig: [
|
||||||
registerComponent('parent', 'parent { <ng-outlet></ng-outlet> }', ParentController);
|
{ path: '/user/:name', component: 'userCmp' }
|
||||||
|
],
|
||||||
|
controller: ParentController
|
||||||
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/parent/...', component: ParentController }
|
{ path: '/parent/...', component: 'parentCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<ng-outlet></ng-outlet>');
|
compile('<ng-outlet></ng-outlet>');
|
||||||
|
|
||||||
$router.navigateByUrl('/parent/user/brian');
|
$router.navigateByUrl('/parent/user/brian');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(instanceCount).toBe(1);
|
||||||
expect(elt.text()).toBe('parent { hello brian }');
|
expect(elt.text()).toBe('parent { hello brian }');
|
||||||
|
|
||||||
spy.calls.reset();
|
|
||||||
|
|
||||||
$router.navigateByUrl('/parent/user/igor');
|
$router.navigateByUrl('/parent/user/igor');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(spy).not.toHaveBeenCalled();
|
expect(instanceCount).toBe(1);
|
||||||
expect(elt.text()).toBe('parent { hello igor }');
|
expect(elt.text()).toBe('parent { hello igor }');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should work with nested outlets', function () {
|
it('should work with nested outlets', function () {
|
||||||
var childComponent = registerComponent('childComponent', '<div>inner { <div ng-outlet></div> }</div>', [
|
registerComponent('childCmp', {
|
||||||
{ path: '/b', component: OneController }
|
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||||
]);
|
$routeConfig: [
|
||||||
|
{ path: '/b', component: 'oneCmp' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a/...', component: childComponent }
|
{ path: '/a/...', component: 'childCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div>outer { <div ng-outlet></div> }</div>');
|
compile('<div>outer { <div ng-outlet></div> }</div>');
|
||||||
|
|
||||||
|
@ -128,40 +113,30 @@ describe('navigation', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should work with recursive nested outlets', function () {
|
// TODO: fix this
|
||||||
put('two', '<div>recur { <div ng-outlet></div> }</div>');
|
xit('should work with recursive nested outlets', function () {
|
||||||
|
registerComponent('recurCmp', {
|
||||||
|
template: '<div>recur { <div ng-outlet></div> }</div>',
|
||||||
|
$routeConfig: [
|
||||||
|
{ path: '/recur', component: 'recurCmp' },
|
||||||
|
{ path: '/end', component: 'oneCmp' }
|
||||||
|
]});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/recur', component: TwoController },
|
{ path: '/recur', component: 'recurCmp' },
|
||||||
{ path: '/', component: OneController }
|
{ path: '/', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
compile('<div>root { <div ng-outlet></div> }</div>');
|
compile('<div>root { <div ng-outlet></div> }</div>');
|
||||||
$router.navigateByUrl('/');
|
$router.navigateByUrl('/recur/recur/end');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(elt.text()).toBe('root { one }');
|
expect(elt.text()).toBe('root { one }');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject $scope into the controller constructor', function () {
|
|
||||||
var injectedScope;
|
|
||||||
var UserController = registerComponent('user', '', function ($scope) {
|
|
||||||
injectedScope = $scope;
|
|
||||||
});
|
|
||||||
|
|
||||||
$router.config([
|
|
||||||
{ path: '/user', component: UserController }
|
|
||||||
]);
|
|
||||||
compile('<div ng-outlet></div>');
|
|
||||||
|
|
||||||
$router.navigateByUrl('/user');
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
expect(injectedScope).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should change location path', inject(function ($location) {
|
it('should change location path', inject(function ($location) {
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/user', component: UserController }
|
{ path: '/user', component: 'userCmp' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
@ -178,7 +153,7 @@ describe('navigation', function () {
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/', redirectTo: '/user' },
|
{ path: '/', redirectTo: '/user' },
|
||||||
{ path: '/user', component: UserController }
|
{ path: '/user', component: 'userCmp' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$router.navigateByUrl('/');
|
$router.navigateByUrl('/');
|
||||||
|
@ -189,16 +164,19 @@ describe('navigation', function () {
|
||||||
|
|
||||||
|
|
||||||
it('should change location to the canonical route with nested components', inject(function ($location) {
|
it('should change location to the canonical route with nested components', inject(function ($location) {
|
||||||
var childRouter = registerComponent('childRouter', '<div>inner { <div ng-outlet></div> }</div>', [
|
registerComponent('childRouter', {
|
||||||
{ path: '/old-child', redirectTo: '/new-child' },
|
template: '<div>inner { <div ng-outlet></div> }</div>',
|
||||||
{ path: '/new-child', component: OneController},
|
$routeConfig: [
|
||||||
{ path: '/old-child-two', redirectTo: '/new-child-two' },
|
{ path: '/old-child', redirectTo: '/new-child' },
|
||||||
{ path: '/new-child-two', component: TwoController}
|
{ path: '/new-child', component: 'oneCmp'},
|
||||||
]);
|
{ path: '/old-child-two', redirectTo: '/new-child-two' },
|
||||||
|
{ path: '/new-child-two', component: 'twoCmp'}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/old-parent', redirectTo: '/new-parent' },
|
{ path: '/old-parent', redirectTo: '/new-parent' },
|
||||||
{ path: '/new-parent/...', component: childRouter }
|
{ path: '/new-parent/...', component: 'childRouter' }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
@ -219,7 +197,7 @@ describe('navigation', function () {
|
||||||
|
|
||||||
it('should navigate when the location path changes', inject(function ($location) {
|
it('should navigate when the location path changes', inject(function ($location) {
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/one', component: OneController }
|
{ path: '/one', component: 'oneCmp' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -232,18 +210,18 @@ describe('navigation', function () {
|
||||||
|
|
||||||
it('should expose a "navigating" property on $router', inject(function ($q) {
|
it('should expose a "navigating" property on $router', inject(function ($q) {
|
||||||
var defer;
|
var defer;
|
||||||
var pendingActivate = registerComponent('pendingActivate', '', {
|
registerComponent('pendingActivate', {
|
||||||
onActivate: function () {
|
$canActivate: function () {
|
||||||
defer = $q.defer();
|
defer = $q.defer();
|
||||||
return defer.promise;
|
return defer.promise;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/pendingActivate', component: pendingActivate }
|
{ path: '/pending-activate', component: 'pendingActivate' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
$router.navigateByUrl('/pendingActivate');
|
$router.navigateByUrl('/pending-activate');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect($router.navigating).toBe(true);
|
expect($router.navigating).toBe(true);
|
||||||
defer.resolve();
|
defer.resolve();
|
||||||
|
@ -251,40 +229,31 @@ describe('navigation', function () {
|
||||||
expect($router.navigating).toBe(false);
|
expect($router.navigating).toBe(false);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
function registerComponent(name, options) {
|
||||||
|
var controller = options.controller || function () {};
|
||||||
|
|
||||||
function registerComponent(name, template, config) {
|
['$onActivate', '$onDeactivate', '$onReuse', '$canReuse', '$canDeactivate'].forEach(function (hookName) {
|
||||||
var Ctrl;
|
if (options[hookName]) {
|
||||||
if (!template) {
|
controller.prototype[hookName] = options[hookName];
|
||||||
template = '';
|
|
||||||
}
|
|
||||||
if (!config) {
|
|
||||||
Ctrl = function () {};
|
|
||||||
} else if (angular.isArray(config)) {
|
|
||||||
Ctrl = function () {};
|
|
||||||
Ctrl.annotations = [new angular.annotations.RouteConfig(config)];
|
|
||||||
} else if (typeof config === 'function') {
|
|
||||||
Ctrl = config;
|
|
||||||
} else {
|
|
||||||
Ctrl = function () {};
|
|
||||||
if (config.canActivate) {
|
|
||||||
Ctrl.$canActivate = config.canActivate;
|
|
||||||
delete config.canActivate;
|
|
||||||
}
|
}
|
||||||
Ctrl.prototype = config;
|
});
|
||||||
|
|
||||||
|
function factory() {
|
||||||
|
return {
|
||||||
|
template: options.template || '',
|
||||||
|
controllerAs: name,
|
||||||
|
controller: controller
|
||||||
|
};
|
||||||
}
|
}
|
||||||
$controllerProvider.register(componentControllerName(name), Ctrl);
|
|
||||||
put(name, template);
|
|
||||||
return Ctrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function boringController(model, value) {
|
if (options.$canActivate) {
|
||||||
return function () {
|
factory.$canActivate = options.$canActivate;
|
||||||
this[model] = value;
|
}
|
||||||
};
|
if (options.$routeConfig) {
|
||||||
}
|
factory.$routeConfig = options.$routeConfig;
|
||||||
|
}
|
||||||
|
|
||||||
function put(name, template) {
|
$compileProvider.directive(name, factory);
|
||||||
$templateCache.put(componentTemplatePath(name), [200, template, {}]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(template) {
|
function compile(template) {
|
||||||
|
|
|
@ -1,44 +1,36 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('ngOutlet', function () {
|
describe('ngLink', function () {
|
||||||
|
|
||||||
var elt,
|
var elt,
|
||||||
$compile,
|
$compile,
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$router,
|
$router,
|
||||||
$templateCache,
|
$compileProvider;
|
||||||
$controllerProvider;
|
|
||||||
|
|
||||||
var OneController, TwoController, UserController;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
module('ng');
|
module('ng');
|
||||||
module('ngComponentRouter');
|
module('ngComponentRouter');
|
||||||
module(function (_$controllerProvider_) {
|
module(function (_$compileProvider_) {
|
||||||
$controllerProvider = _$controllerProvider_;
|
$compileProvider = _$compileProvider_;
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function (_$compile_, _$rootScope_, _$router_, _$templateCache_) {
|
inject(function (_$compile_, _$rootScope_, _$router_) {
|
||||||
$compile = _$compile_;
|
$compile = _$compile_;
|
||||||
$rootScope = _$rootScope_;
|
$rootScope = _$rootScope_;
|
||||||
$router = _$router_;
|
$router = _$router_;
|
||||||
$templateCache = _$templateCache_;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
UserController = registerComponent('user', '<div>hello {{user.name}}</div>', function ($routeParams) {
|
registerComponent('userCmp', '<div>hello {{userCmp.$routeParams.name}}</div>', function () {});
|
||||||
this.name = $routeParams.name;
|
registerComponent('oneCmp', '<div>{{oneCmp.number}}</div>', function () {this.number = 'one'});
|
||||||
});
|
registerComponent('twoCmp', '<div><a ng-link="[\'/Two\']">{{twoCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||||
OneController = registerComponent('one', '<div>{{one.number}}</div>', boringController('number', 'one'));
|
|
||||||
TwoController = registerComponent('two', '<div>{{two.number}}</div>', boringController('number', 'two'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should allow linking from the parent to the child', function () {
|
it('should allow linking from the parent to the child', function () {
|
||||||
put('one', '<div>{{number}}</div>');
|
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: OneController },
|
{ path: '/a', component: 'oneCmp' },
|
||||||
{ path: '/b', component: TwoController, as: 'Two' }
|
{ path: '/b', component: 'twoCmp', as: 'Two' }
|
||||||
]);
|
]);
|
||||||
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
|
compile('<a ng-link="[\'/Two\']">link</a> | outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
|
@ -49,15 +41,13 @@ describe('ngOutlet', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow linking from the child and the parent', function () {
|
it('should allow linking from the child and the parent', function () {
|
||||||
put('one', '<div><a ng-link="[\'/Two\']">{{number}}</a></div>');
|
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: OneController },
|
{ path: '/a', component: 'oneCmp' },
|
||||||
{ path: '/b', component: TwoController, as: 'Two' }
|
{ path: '/b', component: 'twoCmp', as: 'Two' }
|
||||||
]);
|
]);
|
||||||
compile('outer { <div ng-outlet></div> }');
|
compile('outer { <div ng-outlet></div> }');
|
||||||
|
|
||||||
$router.navigateByUrl('/a');
|
$router.navigateByUrl('/b');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
expect(elt.find('a').attr('href')).toBe('./b');
|
expect(elt.find('a').attr('href')).toBe('./b');
|
||||||
|
@ -65,12 +55,11 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
|
|
||||||
it('should allow params in routerLink directive', function () {
|
it('should allow params in routerLink directive', function () {
|
||||||
put('router', '<div>outer { <div ng-outlet></div> }</div>');
|
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'two'});
|
||||||
put('one', '<div><a ng-link="[\'/Two\', {param: \'lol\'}]">{{number}}</a></div>');
|
|
||||||
|
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: OneController },
|
{ path: '/a', component: 'twoLinkCmp' },
|
||||||
{ path: '/b/:param', component: TwoController, as: 'Two' }
|
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
|
@ -80,33 +69,34 @@ describe('ngOutlet', function () {
|
||||||
expect(elt.find('a').attr('href')).toBe('./b/lol');
|
expect(elt.find('a').attr('href')).toBe('./b/lol');
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: test dynamic links
|
|
||||||
it('should update the href of links with bound params', function () {
|
|
||||||
put('router', '<div>outer { <div ng-outlet></div> }</div>');
|
|
||||||
put('one', '<div><a ng-link="[\'/Two\', {param: one.number}]">{{one.number}}</a></div>');
|
|
||||||
|
|
||||||
|
it('should update the href of links with bound params', function () {
|
||||||
|
registerComponent('twoLinkCmp', '<div><a ng-link="[\'/Two\', {param: twoLinkCmp.number}]">{{twoLinkCmp.number}}</a></div>', function () {this.number = 'param'});
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/a', component: OneController },
|
{ path: '/a', component: 'twoLinkCmp' },
|
||||||
{ path: '/b/:param', component: TwoController, as: 'Two' }
|
{ path: '/b/:param', component: 'twoCmp', as: 'Two' }
|
||||||
]);
|
]);
|
||||||
compile('<div ng-outlet></div>');
|
compile('<div ng-outlet></div>');
|
||||||
|
|
||||||
$router.navigateByUrl('/a');
|
$router.navigateByUrl('/a');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
expect(elt.find('a').attr('href')).toBe('./b/one');
|
expect(elt.find('a').attr('href')).toBe('./b/param');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should navigate on left-mouse click when a link url matches a route', function () {
|
it('should navigate on left-mouse click when a link url matches a route', function () {
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/', component: OneController },
|
{ path: '/', component: 'oneCmp' },
|
||||||
{ path: '/two', component: TwoController }
|
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
compile('<a href="/two">link</a> | <div ng-outlet></div>');
|
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(elt.text()).toBe('link | one');
|
expect(elt.text()).toBe('link | one');
|
||||||
|
|
||||||
|
expect(elt.find('a').attr('href')).toBe('./two');
|
||||||
|
|
||||||
elt.find('a')[0].click();
|
elt.find('a')[0].click();
|
||||||
|
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
@ -116,11 +106,11 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($router) {
|
it('should not navigate on non-left mouse click when a link url matches a route', inject(function ($router) {
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/', component: OneController },
|
{ path: '/', component: 'oneCmp' },
|
||||||
{ path: '/two', component: TwoController }
|
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
compile('<a href="./two">link</a> | <div ng-outlet></div>');
|
compile('<a ng-link="[\'/Two\']">link</a> | <div ng-outlet></div>');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(elt.text()).toBe('link | one');
|
expect(elt.text()).toBe('link | one');
|
||||||
elt.find('a').triggerHandler({ type: 'click', which: 3 });
|
elt.find('a').triggerHandler({ type: 'click', which: 3 });
|
||||||
|
@ -133,8 +123,8 @@ describe('ngOutlet', function () {
|
||||||
// See https://github.com/angular/router/issues/206
|
// See https://github.com/angular/router/issues/206
|
||||||
it('should not navigate a link without an href', function () {
|
it('should not navigate a link without an href', function () {
|
||||||
$router.config([
|
$router.config([
|
||||||
{ path: '/', component: OneController },
|
{ path: '/', component: 'oneCmp' },
|
||||||
{ path: '/two', component: TwoController }
|
{ path: '/two', component: 'twoCmp', as: 'Two'}
|
||||||
]);
|
]);
|
||||||
expect(function () {
|
expect(function () {
|
||||||
compile('<a>link</a>');
|
compile('<a>link</a>');
|
||||||
|
@ -147,38 +137,29 @@ describe('ngOutlet', function () {
|
||||||
|
|
||||||
|
|
||||||
function registerComponent(name, template, config) {
|
function registerComponent(name, template, config) {
|
||||||
var Ctrl;
|
var controller = function () {};
|
||||||
|
|
||||||
|
function factory() {
|
||||||
|
return {
|
||||||
|
template: template,
|
||||||
|
controllerAs: name,
|
||||||
|
controller: controller
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
template = '';
|
template = '';
|
||||||
}
|
}
|
||||||
if (!config) {
|
if (angular.isArray(config)) {
|
||||||
Ctrl = function () {};
|
factory.annotations = [new angular.annotations.RouteConfig(config)];
|
||||||
} else if (angular.isArray(config)) {
|
|
||||||
Ctrl = function () {};
|
|
||||||
Ctrl.annotations = [new angular.annotations.RouteConfig(config)];
|
|
||||||
} else if (typeof config === 'function') {
|
} else if (typeof config === 'function') {
|
||||||
Ctrl = config;
|
controller = config;
|
||||||
} else {
|
} else if (typeof config === 'object') {
|
||||||
Ctrl = function () {};
|
|
||||||
if (config.canActivate) {
|
if (config.canActivate) {
|
||||||
Ctrl.$canActivate = config.canActivate;
|
controller.$canActivate = config.canActivate;
|
||||||
delete config.canActivate;
|
|
||||||
}
|
}
|
||||||
Ctrl.prototype = config;
|
|
||||||
}
|
}
|
||||||
$controllerProvider.register(componentControllerName(name), Ctrl);
|
$compileProvider.directive(name, factory);
|
||||||
put(name, template);
|
|
||||||
return Ctrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function boringController(model, value) {
|
|
||||||
return function () {
|
|
||||||
this[model] = value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function put(name, template) {
|
|
||||||
$templateCache.put(componentTemplatePath(name), [200, template, {}]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(template) {
|
function compile(template) {
|
||||||
|
|
|
@ -16,11 +16,6 @@ function dashCase(str) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function boringController (model, value) {
|
|
||||||
return function () {
|
|
||||||
this[model] = value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function provideHelpers(fn, preInject) {
|
function provideHelpers(fn, preInject) {
|
||||||
return function () {
|
return function () {
|
||||||
|
|
Loading…
Reference in New Issue