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.
2015-09-18 15:53:50 -07:00
|
|
|
|
|
|
|
|
|
angular.module('ngComponentRouter').
|
|
|
|
|
value('$route', null). // can be overloaded with ngRouteShim
|
2015-11-01 14:31:36 -06:00
|
|
|
|
// Because Angular 1 has no notion of a root component, we use an object with unique identity
|
|
|
|
|
// to represent this. Can be overloaded with a component name
|
|
|
|
|
value('$routerRootComponent', new Object()).
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
|
2016-03-21 11:49:37 +00:00
|
|
|
|
// Unfortunately, $location doesn't expose what the current hashPrefix is
|
|
|
|
|
// So we have to monkey patch the $locationProvider to capture this value
|
|
|
|
|
provider('$locationHashPrefix', ['$locationProvider', $locationHashPrefixProvider]).
|
|
|
|
|
factory('$rootRouter', ['$q', '$location', '$browser', '$rootScope', '$injector', '$routerRootComponent', '$locationHashPrefix', routerFactory]);
|
|
|
|
|
|
|
|
|
|
function $locationHashPrefixProvider($locationProvider) {
|
|
|
|
|
|
|
|
|
|
// Get hold of the original hashPrefix method
|
|
|
|
|
var hashPrefixFn = $locationProvider.hashPrefix.bind($locationProvider);
|
|
|
|
|
|
|
|
|
|
// Read the current hashPrefix (in case it was set before this monkey-patch occurred)
|
|
|
|
|
var hashPrefix = hashPrefixFn();
|
|
|
|
|
|
|
|
|
|
// Override the helper so that we can read any changes to the prefix (after this monkey-patch)
|
|
|
|
|
$locationProvider.hashPrefix = function(prefix) {
|
|
|
|
|
if (angular.isDefined(prefix)) {
|
|
|
|
|
hashPrefix = prefix;
|
|
|
|
|
}
|
|
|
|
|
return hashPrefixFn(prefix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the final hashPrefix as the value of this service
|
|
|
|
|
this.$get = function() { return hashPrefix; };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function routerFactory($q, $location, $browser, $rootScope, $injector, $routerRootComponent, $locationHashPrefix) {
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
|
|
|
|
|
// When this file is processed, the line below is replaced with
|
|
|
|
|
// the contents of `../lib/facades.es5`.
|
|
|
|
|
//{{FACADES}}
|
|
|
|
|
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
|
var exports = {
|
|
|
|
|
Injectable: function () {},
|
|
|
|
|
OpaqueToken: function () {},
|
|
|
|
|
Inject: function () {}
|
|
|
|
|
};
|
2016-02-11 21:54:29 -06:00
|
|
|
|
var routerRequire = function () {return exports;};
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
|
|
|
|
|
// When this file is processed, the line below is replaced with
|
|
|
|
|
// the contents of the compiled TypeScript classes.
|
|
|
|
|
//{{SHARED_CODE}}
|
|
|
|
|
|
2016-02-25 13:03:29 +00:00
|
|
|
|
function getComponentConstructor(name) {
|
|
|
|
|
var serviceName = name + 'Directive';
|
|
|
|
|
if ($injector.has(serviceName)) {
|
|
|
|
|
var definitions = $injector.get(serviceName);
|
|
|
|
|
if (definitions.length > 1) {
|
|
|
|
|
throw new BaseException('too many directives named "' + name + '"');
|
|
|
|
|
}
|
|
|
|
|
return definitions[0].controller;
|
|
|
|
|
} else {
|
|
|
|
|
throw new BaseException('directive "' + name + '" is not registered');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
//TODO: this is a hack to replace the exiting implementation at run-time
|
|
|
|
|
exports.getCanActivateHook = function (directiveName) {
|
2016-02-25 13:03:29 +00:00
|
|
|
|
var controller = getComponentConstructor(directiveName);
|
|
|
|
|
return controller.$canActivate && function (next, prev) {
|
|
|
|
|
return $injector.invoke(controller.$canActivate, null, {
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
$nextInstruction: next,
|
|
|
|
|
$prevInstruction: prev
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// This hack removes assertions about the type of the "component"
|
|
|
|
|
// property in a route config
|
|
|
|
|
exports.assertComponentExists = function () {};
|
|
|
|
|
|
refactor(router): improve recognition and generation pipeline
This is a big change. @matsko also deserves much of the credit for the implementation.
Previously, `ComponentInstruction`s held all the state for async components.
Now, we introduce several subclasses for `Instruction` to describe each type of navigation.
BREAKING CHANGE:
Redirects now use the Link DSL syntax. Before:
```
@RouteConfig([
{ path: '/foo', redirectTo: '/bar' },
{ path: '/bar', component: BarCmp }
])
```
After:
```
@RouteConfig([
{ path: '/foo', redirectTo: ['Bar'] },
{ path: '/bar', component: BarCmp, name: 'Bar' }
])
```
BREAKING CHANGE:
This also introduces `useAsDefault` in the RouteConfig, which makes cases like lazy-loading
and encapsulating large routes with sub-routes easier.
Previously, you could use `redirectTo` like this to expand a URL like `/tab` to `/tab/posts`:
@RouteConfig([
{ path: '/tab', redirectTo: '/tab/users' }
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
Now the recommended way to handle this is case is to use `useAsDefault` like so:
```
@RouteConfig([
{ path: '/tab', component: TabsCmp, name: 'Tab' }
])
AppCmp { ... }
@RouteConfig([
{ path: '/posts', component: PostsCmp, useAsDefault: true, name: 'Posts' },
{ path: '/users', component: UsersCmp, name: 'Users' }
])
TabsCmp { ... }
```
In the above example, you can write just `['/Tab']` and the route `Users` is automatically selected as a child route.
Closes #4728
Closes #4228
Closes #4170
Closes #4490
Closes #4694
Closes #5200
Closes #5475
2015-11-23 18:07:37 -08:00
|
|
|
|
angular.stringifyInstruction = function (instruction) {
|
|
|
|
|
return instruction.toRootUrl();
|
|
|
|
|
};
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
|
|
|
|
|
var RouteRegistry = exports.RouteRegistry;
|
|
|
|
|
var RootRouter = exports.RootRouter;
|
|
|
|
|
|
2016-02-25 13:03:29 +00:00
|
|
|
|
// Override this method to actually get hold of the child routes
|
|
|
|
|
RouteRegistry.prototype.configFromComponent = function (component) {
|
|
|
|
|
var that = this;
|
|
|
|
|
if (isString(component)) {
|
|
|
|
|
// Don't read the annotations component a type more than once –
|
|
|
|
|
// this prevents an infinite loop if a component routes recursively.
|
|
|
|
|
if (this._rules.has(component)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var controller = getComponentConstructor(component);
|
|
|
|
|
if (angular.isArray(controller.$routeConfig)) {
|
|
|
|
|
controller.$routeConfig.forEach(function (config) {
|
|
|
|
|
var loader = config.loader;
|
|
|
|
|
if (isPresent(loader)) {
|
2016-04-14 17:23:55 -05:00
|
|
|
|
config = angular.extend({}, config, { loader: function() { return $injector.invoke(loader); } });
|
2016-02-25 13:03:29 +00:00
|
|
|
|
}
|
|
|
|
|
that.config(component, config);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-01 14:31:36 -06:00
|
|
|
|
var registry = new RouteRegistry($routerRootComponent);
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
var location = new Location();
|
|
|
|
|
|
2015-11-01 14:31:36 -06:00
|
|
|
|
var router = new RootRouter(registry, location, $routerRootComponent);
|
2016-01-26 13:51:50 +01:00
|
|
|
|
$rootScope.$watch(function () { return $location.url(); }, function (path) {
|
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.
2015-09-18 15:53:50 -07:00
|
|
|
|
if (router.lastNavigationAttempt !== path) {
|
|
|
|
|
router.navigateByUrl(path);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.subscribe(function () {
|
|
|
|
|
$rootScope.$broadcast('$routeChangeSuccess', {});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return router;
|
|
|
|
|
}
|