In this step, you will learn how to create a layout template and how to build an app that has multiple views by adding routing, using an Angular 1 module called ui-router
.
The goals for this step:
index.html
, you will be redirected to /parties
and the party list should appear in the browser.The routing functionality added by this step is provided by the ui-router module, which is distributed separately from the core Angular 1 framework.
Type in the command line:
meteor npm install --save angular-ui-router
8
9
10
11
12
13
14
"dependencies": {
"angular": "^1.5.3",
"angular-meteor": "^1.3.9",
"angular-ui-router": "^0.2.18",
"meteor-node-stubs": "~0.2.0"
},
"devDependencies": {
Then add the ui-router as a dependency to our Socially app in socially.js
:
1
2
3
4
5
6
12
13
14
15
16
17
18
import angular from 'angular';
import angularMeteor from 'angular-meteor';
import uiRouter from 'angular-ui-router';
import template from './socially.html';
import { name as PartiesList } from '../partiesList/partiesList';
...some lines skipped...
// create a module
export default angular.module(name, [
angularMeteor,
uiRouter,
PartiesList
]).component(name, {
template,
Our app is slowly growing and becoming more complex.
Until now, the app provided our users with a single view (the list of all parties), and all of the template code was located in the main.html
file.
The next step in building the app is to add a view that will show detailed information about each of the parties on our list.
To add the detailed view, we could expand the main.html
file to contain template code for both views, but that would get messy very quickly.
Instead, we are going to turn the index.html
template into what we call a "layout template". This is a template that is common for all views in our application.
Other "partial templates" are then included into this layout template depending on the current "route" — the view that is currently displayed to the user.
Application routes in Angular 1 are declared via the $stateProvider, which is the provider of the $state service. This service makes it easy to wire together controllers, view templates, and the current URL location in the browser. Using this feature we can implement deep linking, which lets us utilize the browser's history (back and forward navigation) and bookmarks.
The $state
service is normally used in conjunction with the uiView directive.
The role of the ui-view
directive is to include the view template for the current route into the layout template.
This makes it a perfect fit for our main.html
file.
Now let's go back to index.html
to add base tag to our main html file:
1
2
3
4
5
6
<head>
<base href="/">
</head>
<body ng-app="socially" ng-strict-di="">
<socially></socially>
</body>
We still need to add uiView directive, Socially is the best place for it:
1
<div ui-view=""></div>
Let's define a default route and use html5 mode to make urls look a lot fancier:
18
19
20
21
22
23
24
25
26
27
28
29
30
template,
controllerAs: name,
controller: Socially
})
.config(config);
function config($locationProvider, $urlRouterProvider) {
'ngInject';
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/parties');
}
It would be nice to have a navigation. Create one! Let's call our new component Navigation
:
1
2
3
<h1>
<a href="/parties">Home</a>
</h1>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import angular from 'angular';
import angularMeteor from 'angular-meteor';
import template from './navigation.html';
const name = 'navigation';
// create a module
export default angular.module(name, [
angularMeteor
]).component(name, {
template,
controllerAs: name
});
And implement it in Socially
:
1
2
3
<navigation></navigation>
<div ui-view=""></div>
4
5
6
7
8
9
10
14
15
16
17
18
19
20
21
import template from './socially.html';
import { name as PartiesList } from '../partiesList/partiesList';
import { name as Navigation } from '../navigation/navigation';
class Socially {}
...some lines skipped...
export default angular.module(name, [
angularMeteor,
uiRouter,
PartiesList,
Navigation
]).component(name, {
template,
controllerAs: name,
Notice we did 3 things:
base
tag in the head (required when using HTML5 location mode - would be explained a bit further).Note that you can remove
main.html
now, because it's no longer in use!
Now let's configure our routes.
There are no states at this stage, so let's add parties
inside PartiesList
component:
1
2
3
4
5
6
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import angular from 'angular';
import angularMeteor from 'angular-meteor';
import uiRouter from 'angular-ui-router';
import template from './partiesList.html';
import { Parties } from '../../../api/parties';
...some lines skipped...
// create a module
export default angular.module(name, [
angularMeteor,
uiRouter,
PartyAdd,
PartyRemove
]).component(name, {
template,
controllerAs: name,
controller: PartiesList
})
.config(config);
function config($stateProvider) {
'ngInject';
$stateProvider
.state('parties', {
url: '/parties',
template: '<parties-list></parties-list>'
});
}
And we will also add a state for a new page that will display the party details.
Our application routes are defined as follows:
:partyId
is a variable part of the URL. To construct the party details view, Angular will use the party-details Component.Note the use of the :partyId
parameter in the second route declaration.
The $state service uses the route declaration — /parties/:partyId
— as a template that is matched against the current URL.
All variables defined with the : notation are passed into the Component through the $stateParams
object.
I mentioned a state with party details. We have to define our partyDetails
component.
Let's create the view for this Component in a new file:
1
The party you selected is: {{ partyDetails.partyId }}
Now we can create actual component:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import angular from 'angular';
import angularMeteor from 'angular-meteor';
import uiRouter from 'angular-ui-router';
import template from './partyDetails.html';
class PartyDetails {
constructor($stateParams) {
'ngInject';
this.partyId = $stateParams.partyId;
}
}
const name = 'partyDetails';
// create a module
export default angular.module(name, [
angularMeteor
]).component(name, {
template,
controllerAs: name,
controller: PartyDetails
});
And also define new route which was mentioned before as /parties/:partyId
:
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// create a module
export default angular.module(name, [
angularMeteor,
uiRouter
]).component(name, {
template,
controllerAs: name,
controller: PartyDetails
})
.config(config);
function config($stateProvider) {
'ngInject';
$stateProvider.state('partyDetails', {
url: '/parties/:partyId',
template: '<party-details></party-details>'
});
}
Now let's add a link from each party in the parties list to it's details page:
4
5
6
7
8
9
10
16
17
18
19
20
21
22
import template from './socially.html';
import { name as PartiesList } from '../partiesList/partiesList';
import { name as PartyDetails } from '../partyDetails/partyDetails';
import { name as Navigation } from '../navigation/navigation';
class Socially {}
...some lines skipped...
angularMeteor,
uiRouter,
PartiesList,
PartyDetails,
Navigation
]).component(name, {
template,
2
3
4
5
6
7
8
9
10
<ul>
<li ng-repeat="party in partiesList.parties">
<a ui-sref="partyDetails({ partyId: party._id })">
{{party.name}}
</a>
<p>{{party.description}}</p>
<party-remove party="party"></party-remove>
</li>
Now all is in place. Run the app and you'll notice a few things:
With the routing set up and the parties list view implemented, we're ready to go to the next step and implement the party details view.