.l-main-section
h2#section-route-use Using the Component Router
p There are three steps to setting up routing with Angular's Component Router
ol
li Install the Component Router
li Map paths to components
li Link to routes
.l-main-section
h2#section-install-router Import the Component Router
p.
Create two files, index.html
and app.ts
, both at the root of the project:
pre.prettyprint.lang-bash
code.
touch index.html app.ts
p Your app directory should look something like:
pre.prettyprint.lang-bash
code.
app.ts
index.html
package.json
node_modules/
└── ...
p.
Because the component is an addition to the core, you must install Angular's Component Router into your app.
When using the angular2.dev.js bundle you have include the additional router.dev.js bundle.
p.
Add Angular and Component Router into your app by adding the relevant <script>
tags into your
index.html
:
//ANGULAR 1
pre.prettyprint.lang-html.is-angular1.is-hidden
code.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<base href="/">
<title>My app</title>
</head>
<body ng-app="myApp" ng-controller="AppController as app">
<div ng-outlet></div>
<script src="/node_modules/angular/angular.js"></script>
<script src="/dist/router.es5.js"></script>
<script src="/app/app.js"></script>
</body>
</html>
pre.prettyprint.lang-html.is-angular2
code.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<base href="/">
<title>My app</title>
</head>
<body ng-app="myApp" ng-controller="AppController as app">
<script src="https://jspm.io/system@0.16.js"></script>
<script src="https://code.angularjs.org/2.0.0-alpha.21/angular2.dev.js"></script>
<script src="https://code.angularjs.org/2.0.0-alpha.21/router.dev.js"></script>
<script>
System.import('main');
</script>
</body>
</html>
p.is-angular2.
Then you can add the router into your app by importing the Router module in your app.ts
file:
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
import {Component, View, bootstrap} from 'angular2/angular2';
import {Router, RouterOutlet, RouterLink} from 'angular2/router';
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
// self-executing bundle deploys angular APIs on the window object.
window.angular;
// the router APIs are part of the router sub-object.
window.angular.router;
p.is-angular1.is-hidden.
This is a pretty typical angular app, except the ng-outlet
directive. ng-outlet
is like
ng-view
; it's a placeholder for part of your app loaded dynamically based on the route configuration.
p.is-angular2.
This is the same as you've seen in the rest of Angular 2, except the router-outlet
directive.
router-outlet
is a placeholder for part of your app loaded dynamically based on the route configuration.
p So how do we configure the app? Let's open app.ts
and find out. Add this to the file:
//ANGULAR 1
pre.prettyprint.lang-javascript.is-angular1.is-hidden
code.
angular.module('app', ['ngNewRouter'])
.controller('AppController', ['$router', AppController]);
AppController.$routeConfig = [
{path: '/', component: 'home' }
];
function AppController ($router) {}
// ANGULAR 2
.code-box
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
import {Component, View, bootstrap} from 'angular2/angular2';
import {routerInjectables, RouterOutlet} from 'angular2/router';
import {HomeComp} from './components/home';
@Component({
selector: 'my-app'
})
@View({
template: '<router-outlet></router-outlet>',
directives: [RouterOutlet]
})
@RouteConfig([
{path: '/', component: HomeComp }
])
class AppComp {}
bootstrap(AppComp, routerInjectables);
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
var HomeComp = function() {};
...
var AppComp = function() {};
AppComp.annotations = [
new angular.ComponentAnnotation({
selector: 'my-app'
}),
new angular.ViewAnnotation({
template: '<router-outlet></router-outlet>',
directives: [angular.router.RouterOutlet]
}),
new angular.router.RouteConfigAnnotation([
{path: '/', component: HomeComp}
])
];
angular.bootstrap(AppComp, routerInjectables);
p.is-angular1.is-hidden.
The ngComponentRouter
module provides a new service, $router
. In the configuration, we map paths
to components. What's a component? Let's talk about that for a bit.
p.is-angular2.
The angular2/router
module provides routerInjectables
, which is an array of all of the services
you'll need to use the component router in your app.
.l-main-section
h2#section-map-paths-to-components Map paths to components
//- TODO - Alex - would it make more sense to have some paragraph styles conditionalized like this??
p.angular1.is-hidden.
In Angular 1, a "routable component" is a template, plus a controller, plus a router. You can configure how to map
component names to controllers and templates in the $componentLoader
service.
p.
A component's template can have "outlets," which are holes in the DOM for loading parts of your app based on the
route configuration and it can ask the DI system for an instance of Router. A component's router tells the component what to put
inside the outlets based on URL. The configuration maps routes to components for each outlet.
p Let's make a home
component that our app can route to:
pre.prettyprint.lang-bash
code.
mkdir -p components/home
touch components/home/home.html components/home/home.js
p This creates our component directory and its corresponding files: a template and a JavaScript component.
p Let's open home.html
and add some content:
pre.prettyprint.lang-html
code.
<h1>Hello {{home.name}}!</h1>
p.is-angular1.is-hidden.
Components use the "controller as" syntax, so if we want to access property name
of the controller, we
write the binding as home.name
.
p Let's make a controller:
//ANGULAR 1
pre.prettyprint.lang-javascript.is-angular1.is-hidden
code.
angular.module('app.home', [])
.controller('HomeController', [function () {
this.name = 'Friend';
}]);
// ANGULAR 2
.code-box
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
@Component({
selector: 'home-cmp'
})
@View({
template: 'Hello {{name}}'
})
export class HomeComponent {
name:string;
constructor() {
this.name = 'Friend';
}
}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
function HomeComponent() {
this.name = 'Friend';
}
AppComponent.annotations = [
new angular.ComponentAnnotation({
selector: 'home-cmp'
}),
new angular.ViewAnnotation({
template: 'Hello {{name}}'
})
];
p.is-angular1.is-hidden.
To wire this up, we need to add a <script>
tag to our index.html
:
pre.prettyprint.lang-html
code.
...
<script src="./components/home/home.js"></script>
//ANGULAR 1
p.is-angular1.is-hidden.
And add the controller's module as a dependency to our main module in app.js
:
pre.prettyprint.lang-javascript.is-angular1.is-hidden
code.
angular.module('app', ['ngNewRouter', 'app.home'])
.controller('AppController', ['$router', AppController]);
// ...
p.
To wire this up, we need to import the component into the rest of our app.
// ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
import {HomeComp} from './components/home';
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
// Use your favorite module system / bundler for ES5.
p If you load up the app, you should see Hello Friend!
.l-main-section
h2#section-link-to-routes Link to routes
p Let's add another route and then link to it. This route will have a route parameter, id
.
p In app.js
:
//ANGULAR 1
pre.prettyprint.lang-javascript.is-hidden
code.
angular.module('app', ['ngNewRouter'])
.controller('AppController', ['$router', AppController]);
AppController.$routeConfig = [
{ path: '/', component: 'home' },
{ path: '/detail/:id', component: 'detail' }
];
function AppController ($router) {}
// ANGULAR 2
.code-box
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
...
@RouteConfig([
{ path: '/', component: HomeComp },
{ path: '/detail/:id', component: DetailComp }
])
class AppComp {}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
var AppComp = function() {};
AppComp.annotations = [
...
new angular.router.RouteConfigAnnotation([
{ path: '/', component: HomeComp}
{ path: '/detail/:id', component: DetailComp }
])
];
angular.bootstrap(AppComp, routerInjectables);
p.
We can link to our detail component using the
router-link
ng-link
directive.
Add this to template
index.html
:
pre.prettyprint.lang-html.is-angular1.is-hidden.
code.
<body ng-app="myApp" ng-controller="AppController as app">
<a ng-link="detail({id: 5})">link to detail</a>
...
pre.prettyprint.lang-html.is-angular2
code.
<a ng-link="detail({id: 5})">link to detail</a>
p This directive will generate an href
and update the browser URL.
p We should also implement our detail component. Let's make these new files:
pre.prettyprint.lang-bash
code.
mkdir components/detail
touch components/detail/detail.html components/detail/detail.ts
p In detail.ts
, we implement a controller that uses the id
route parameter:
//ANGULAR 1
pre.prettyprint.lang-javascript.is-hidden
code.
angular.module('app.detail', ['ngNewRouter'])
.controller('DetailController', ['$routeParams', DetailController]);
function DetailController ($routeParams) {
this.id = $routeParams.id;
}
// ANGULAR 2
.code-box
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
@Component({
selector: 'detail-cmp'
})
@View({
template: 'User ID: {{id}}'
})
export class DetailComp {
id: string;
constructor(routeParams:RouteParams) {
this.id = routeParams.get('id');
}
}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
function DetailComp(routeParams) {
this.id = routeParams.get('id');
}
DetailComp.annotations = [
new angular.ComponentAnnotation({
selector: 'detail-cmp'
}),
new angular.ViewAnnotation({
template: 'User ID: {{id}}'
})
];
DetailComp.parameters = [[RouteParams]];
p.is-angular1.is-hidden.
And then we can display the id
in our template by adding this to detail.html
:
pre.prettyprint.lang-html.is-angular1.is-hidden
code.
<p>detail {{detail.id}}</p>
p.is-angular1.is-hidden.
Finally, we'd wire up the controller by adding a script tag and making our app
module depend on
app.detail
.
.l-main-section
h2#section-configuring-the-router Configuring the Router
p.
Unlike other routing systems, Component Router maps URLs to components. A router takes an array of pairings like
this:
//ANGULAR 1
pre.prettyprint.lang-javascript.is-angular1.is-hidden
code.
//ES5
MyController.$routeConfig = [
{ path: '/user', component: 'user' }
];
//ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-javascript(data-name="typescript")
code.
@Component()
@View()
@RouteConfig([
{ path: '/user', component: UserComponent }
])
class MyComp {}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
function MyComp() {};
MyComp.annotations = [
new angular.ComponentAnnotation({ ... }),
new angular.ViewAnnotation({ ... }),
new angular.router.RouteConfigAnnotation([
{path: '/', component: UserComponent}
])
.l-sub-section
h3#section-sibling-outlets Sibling Outlets
p You can configure multiple outlets on the same path like this:
//ANGULAR 1
.codebox.is-angular1.is-hidden
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
//ES5
MyController.$routeConfig = [
{ path: '/user',
components: {
master: 'userList',
detail: 'user'
} }
];
pre.prettyprint.linenums.lang-html(data-name="html")
code.
//HTML
<div ng-outlet="master"></div>
<div ng-outlet="detail"></div>
//ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
//TypeScript
@Component({})
@View({
template:
`<div router-outlet="master"></div>
<div router-outlet="detail"></div>`,
directives: [RouterOutlet, RouterLink]
})
@RouteConfig({
path: '/user', components: {
master: UserListComp,
detail: UserComp
}
})
class MyComponent {}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
function MyComponent() {};
MyComponent.annotations = [
new angular.ComponentAnnotation({ ... }),
new angular.ViewAnnotation({
template:
'<div router-outlet="master"></div>' +
'<div router-outlet="detail"></div>',
directives: [RouterOutlet]
}),
new angular.router.RouteConfigAnnotation([{
path: '/user', components: {
master: UserComponent,
detail: UserComp
}
}])
];
p You can link to any sibling just as you normally would:
//ANGULAR 1
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
code.
//HTML
<p>These both link to the same view:</p>
<a ng-link="userList">link to userList</a>
<a ng-link="user">link to user component</a>
//ANGULAR 2
pre.prettyprint.linenums.lang-html.is-angular2
code.
//HTML
<p>These both link to the same view:</p>
<a router-link="userList">link to userList</a>
<a router-link="user">link to user component</a>
p Or, you can explicitly link to a outlet-component pair like this:
//ANGULAR 1
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
code.
//HTML
<p>These both link to the same view:</p>
<a ng-link="master:userList">link to userList</a>
<a ng-link="detail:user">link to user component</a>
//ANGULAR 2
pre.prettyprint.linenums.lang-html.is-angular2
code.
//HTML
<p>These both link to the same view:</p>
<a router-link="master:userList">link to userList</a>
<a router-link="detail:user">link to user component</a>
.l-sub-section
h3#section-redirecting-routes Redirecting routes
p You can use `redirectTo` for migrating to a new URL scheme and setting up default routes.
p.
For example, as specified below, when a user navigates to `/`, the URL changes to `/user` and the outlet
at that level loads the `user` component.
//ANGULAR 1
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
code.
//ES5
MyController.$routeConfig = [
{ path: '/', redirectTo: '/user' },
{ path: '/user', component: 'user' }
];
function MyController() {}
//ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
//TypeScript
@Component({})
@View({
directives: [RouterOutlet]
})
@RouteConfig([
{ path: '/', redirectTo: '/user' },
{ path: '/user', component: UserComp }
])
class MyComp {}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
function MyComponent() {};
MyComponent.annotations = [
new angular.ComponentAnnotation({ ... }),
new angular.ViewAnnotation({
directives: [RouterOutlet]
}),
new angular.router.RouteConfigAnnotation([
{ path: '/user', component: UserComp }
{ path: '/', redirectTo: '/user' },
])
];
.l-sub-section
h3#section-aliases Aliases
p.
When linking to a route, you normally use the name of the component. You can also specify an alias to use
instead.
p Consider the following route configuration:
//ANGULAR 1
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
code.
//ES5
MyController.$routeConfig = [
{ path: '/', component: 'user' }
];
//ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
//TypeScript
@Component({
selector: 'my-comp'
})
@View({
directives: [RouterOutlet]
})
@RouteConfig([
{ path: '/', component: UserComp }
])
class MyComp {}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
function MyComp() {};
MyComp.annotations = [
new angular.ComponentAnnotation({ ... }),
new angular.ViewAnnotation({
directives: [RouterOutlet]
}),
new angular.router.RouteConfigAnnotation([
{ path: '/', component: UserComp }
])
];
p We can link to the route in our template with the name of the component:
//ANGULAR 1
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
code.
//HTML
<a ng-link="user">link to user component</a>
//ANGULAR 2
pre.prettyprint.linenums.lang-html
code.
//HTML
<a router-link="user">link to user component</a>
p Or, we can define an alias myUser
like this:
//ANGULAR 1
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
code.
//ES5
MyController.$routeConfig = [
{ path: '/', component: 'user', as: 'myUser' }
];
//ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
//TypeScript
@Component()
@View()
@RouteConfig([
{ path: '/', component: UserComp, as: 'myUser' }
])
class MyComp {}
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
//ES5
//TODO: Need Angular 2 ES5 Example here
p And refer instead to the alias for the component in our template, with the same end-result:
//ANGULAR 1
pre.prettyprint.linenums.lang-html.is-angular1.is-hidden
code.
//HTML
<a ng-link="myUser">link to user component</a>
//ANGULAR 2
pre.prettyprint.linenums.lang-html
code.
//HTML
<a router-link="myUser">link to user component</a>
p.
This is especially useful when you have sibling components, but want to refer to an entire level of routing in
your controller. For example:
//ANGULAR 1
pre.prettyprint.linenums.lang-javascript.is-angular1.is-hidden(data-name="es5")
code.
//ES5
MyController.$routeConfig = [
{ path: '/',
components: {
master: 'userList',
detail: 'user'
},
as: 'myUser'
}
];
//ANGULAR 2
.code-box.is-angular2
pre.prettyprint.linenums.lang-typescript(data-name="typescript")
code.
//TypeScript
@RouteConfig([
{ path: '/', components:
{ master: UserListComp, detail: UserComp },
as: 'myUser' }
])
pre.prettyprint.linenums.lang-javascript(data-name="es5")
code.
new angular.router.RouteConfigAnnotation([
{ path: '/', components:
{ master: UserListComp, detail: UserComp },
as: 'myUser' }
])
//- TODO(btford): expand on this.
.l-sub-section
h3#dynamic-configuration Dynamic Configuration
p.is-angular2.
You can configure dynamic routing by asking the DI system for a Router
.
p.is-angular1.is-hidden
You can configure dynamic routing by making a request for $router
.