(docs) testing and toh formatting improvements

This commit is contained in:
Ward Bell 2015-10-16 01:06:56 -07:00
parent cdec7d1605
commit 93a646d63f
12 changed files with 160 additions and 118 deletions

View File

@ -3,7 +3,7 @@
// #docregion class-w-annotations
var AppComponent = ng
ng.Component({
.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})

View File

@ -2,6 +2,7 @@
<!-- #docregion -->
<html>
<head>
<title>Angular 2 QuickStart</title>
<script src="node_modules/angular2/bundles/angular2.sfx.dev.js"></script>
<script src="app.js"></script>
</head>

View File

@ -3,7 +3,7 @@
<html>
<head>
<title>Getting Started</title>
<title>Angular 2 QuickStart</title>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-alpha.42/angular2.dev.js"></script>

View File

@ -3,7 +3,7 @@
<html>
<head>
<title>Getting Started</title>
<title>Angular 2 QuickStart</title>
<!-- #docregion libraries -->
<script src="../node_modules/systemjs/dist/system.src.js"></script>
<script src="../node_modules/angular2/bundles/angular2.dev.js"></script>

View File

@ -2,22 +2,27 @@
"_listtype": "ordered",
"index": {
"title": "Testing Guides"
"title": "Testing Guides",
"intro": "Techniques and practices for testing an Angular 2 app"
},
"jasmine-testing-101": {
"title": "Jasmine Testing 101"
"title": "Jasmine Testing 101",
"intro": "The basics of testing anything with Jasmine"
},
"application-under-test": {
"title": "The Application Under Test"
"title": "The Application Under Test",
"intro": "A quick look at the application we will test"
},
"first-app-tests": {
"title": "First App Tests"
"title": "First App Tests",
"intro": "The first test of a simple, non-Angular part of our app"
},
"testing-an-angular-pipe": {
"title": "Testing an Angular Pipe"
"title": "Testing an Angular Pipe",
"intro": "We test an Angular-aware part of our app"
}
}

View File

@ -32,5 +32,7 @@ figure.image-display
Well examine the implementation details as we evolve our tests.
.l-main-section
:markdown
## Whats Next?
Now that were familiar with how the test app works, were ready to poke at it with our first application tests written in Jasmine.

View File

@ -16,6 +16,8 @@ include ../../../../_includes/_util-fns
we introduced in the [QuickStart](../quickstart.html) and
the [Tour of Heroes](../tutorial/) tutorial
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
.l-main-section
:markdown
## Create the test-runner HTML
@ -44,6 +46,8 @@ include ../../../../_includes/_util-fns
Were picking up right where we left off. All weve done is change the title.
.l-main-section
:markdown
## Update `package.json` for testing
Well assume that the application has `package.json` file that looks more or less like
@ -66,6 +70,8 @@ pre.prettyprint.lang-bash
That command will launch `live-server` and open a browser to the `unit-tests.html` page we just wrote.
.l-main-section
:markdown
## First app tests
Believe it or not … we could start testing *some* of our app right away. For example, we can test the `Hero` class:
@ -128,6 +134,8 @@ pre.prettyprint.lang-bash
The description should be sufficient to identify the tested application part and its source file. Almost any convention will do as long as you and your team follow it consistently and are never confused.
.l-main-section
:markdown
## Run the tests
Open one terminal window and run the watching compiler command: `npm run tsc`
@ -139,6 +147,8 @@ pre.prettyprint.lang-bash
figure.image-display
img(src='/resources/images/devguide/first-app-tests/passed-2-specs-0-failures.png' style="width:400px;" alt="Two passing tests")
.l-main-section
:markdown
## Critique
@ -150,6 +160,8 @@ figure.image-display
We need to relocate these tests to a separate file. Lets do that next.
.l-main-section
:markdown
## Where do tests go?
Some people like to keep their tests in a `tests` folder parallel to the application source folder.
@ -165,6 +177,8 @@ figure.image-display
You may put your tests elsewhere if you wish. Were putting ours inside the app, next to the source files that they test.
.l-main-section
:markdown
## First spec file
**Create** a new file, ** `hero.spec.ts` ** in `src/app` next to `hero.ts`.
@ -209,7 +223,7 @@ figure.image-display
<script src="app/hero.js"></script>
<script src="app/hero.spec.js"></script>
## Run and Fail
### Run and Fail
Look over at the browser (live-server will have reloaded it). The browser displays
@ -220,10 +234,12 @@ figure.image-display
Thats Jasmine saying “**things are _so_ bad that _Im not running any tests_.**”
Open the browsers Developer Tools (F12, Ctrl-Shift-i). Theres an error:
```
Uncaught ReferenceError: exports is not defined
```
code-example(format="" language="html").
Uncaught ReferenceError: exports is not defined
.l-main-section
:markdown
## Load tests with systemjs
The immediate cause of the error is the `export` statement in `hero.ts`.
@ -278,6 +294,8 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/first-app-tests/test-passed-once-again.png' style="width:400px;" alt="Tests passed once again")
.l-main-section
:markdown
## Observations
@ -307,6 +325,8 @@ figure.image-display
So we must wait until the import completes and only then call the window `onLoad` handler.
Jasmine re-starts, this time with our imported test queued up.
.l-main-section
:markdown
## Whats Next?
We are able to test a part of our application with simple Jasmine tests.
The part was a stand-alone class that made no mention or use of Angular.

View File

@ -36,7 +36,6 @@ pre.prettyprint.lang-bash
:markdown
The browser is nice during development of a few tests. Its not the best venue for working with a lot of tests and it wont do at all for build automation. Well switch to the karma test-runner when the time comes. But the browser will do for now.
.l-main-section
:markdown
Create a new file called`unit-tests.html` and enter the following:
```
@ -74,6 +73,8 @@ figure.image-display
:markdown
It doesnt get much simpler than that!
.l-main-section
:markdown
## First TypeScript Test
Perhaps too simple. We wont write our entire test suite inside one HTML file.
Lets **extract** that line of test code to a **new file in `src` called `1st.spec.ts` ** .
@ -82,7 +83,6 @@ figure.image-display
:markdown
Among Jasmine developers, a test is known as a “spec” and test filenames include the word “spec”. Well stick with that convention.
.l-main-section
:markdown
The test we wrote is valid TypeScript because any JavaScript is valid TypeScript. But lets make it more modern with an arrow function:
```
@ -96,6 +96,8 @@ figure.image-display
Thats a reminder that we need to compile our TypeScript test files as we do our TypeScript application files. Do that next.
.l-main-section
:markdown
## Prepare for TypeScript
As weve seen before, we first have to tell the compiler how to compile our TypeScript files with
@ -143,6 +145,8 @@ pre.prettyprint.lang-bash
We should get the same Jasmine test-runner output as before.
.l-main-section
:markdown
## Add a describe and another test
We cant tell what file produced these test results. We only have one file at the moment but soon well write more.
@ -189,6 +193,8 @@ figure.image-display
:markdown
We can re-run just the failing test by double-clicking it. Try it!
.l-main-section
:markdown
## Debug the test
Suppose we didnt know what was going on. We can debug it in the browser.
@ -214,10 +220,14 @@ figure.image-display
Congratulations … youve completed Jasmine testing 101.
.l-main-section
:markdown
## Learn more
Learn more about basic Jasmine testing here
[Resources TBD](./#)
.l-main-section
:markdown
## Whats Next?
Now that were familiar with Jasmine on its own, were ready to test an application.

View File

@ -9,10 +9,8 @@ include ../../../../_includes/_util-fns
We use it our `hero-detail.component.html` template to turn a hero name like “eeny weenie” into “Eeny Weenie”
The code for `InitCapsPipe` in `init-caps-pipe.ts` is quite brief:
code-example(format="linenums").
&lt;h2&gt;{{hero.name | initCaps}} is {{userName}}'s current super hero!&lt;/h2&gt;
code-example(format="linenums" language="html" escape="html").
<h2>{{hero.name | initCaps}} is {{userName}}'s current super hero!</h2>
:markdown
The code for `InitCapsPipe` in `init-caps-pipe.ts` is quite brief:
@ -61,9 +59,10 @@ figure.image-display
in the console window, we would see that SystemJS
tried to load Angular and couldn't find it.
```
code-example(format="" language="html" escape="html").
GET http://127.0.0.1:8080/src/angular2/angular2 404 (Not Found)
```
:markdown
We are writing an Angular application afterall and
we were going to need Angular sooner or later. That time has come.
The `InitCapsPiep` clearly depends on Angular as is clear in the first few lines:

View File

@ -25,6 +25,8 @@ include ../../../../_includes/_util-fns
We'll be covering a lot of ground at an introductory level but well find plenty of links
to chapters with greater depth.
.l-main-section
:markdown
## The End Game
Here's a visual idea of where we're going in this tour, beginning with the "Dashboard"
@ -60,6 +62,8 @@ include ../../../../_includes/_util-fns
We can click a hero and have the router race us over to a "Hero Details" view
again.
.l-main-section
:markdown
## How We Roll
Well build this Tour of Heroes together, step by step.

View File

@ -1,7 +1,6 @@
include ../../../../_includes/_util-fns
.l-main-section
:markdown
# Once Upon a Time
@ -24,7 +23,7 @@ include ../../../../_includes/_util-fns
└── package.json
:markdown
## Keep the App Running
### Keep the app running
Start the TypeScript compiler and have it watch for changes in one terminal window by typing
pre.prettyprint.lang-bash
@ -40,7 +39,7 @@ include ../../../../_includes/_util-fns
This command starts the server, launches the app in a browser,
and keeps the app running while we continue to build the Tour of Heroes.
.alert.is-helpful
.l-sub-section
:markdown
These two steps watch all project files. They recompile TypeScript files and re-run
the app when any file changes.
@ -48,9 +47,8 @@ include ../../../../_includes/_util-fns
stop these commands in each terminal by typing `CTRL+C` and then re-run them.
.l-main-section
:markdown
# Show our Hero
## Show our Hero
We want to display Hero data in our app
Let's add two properties to our `AppComponent`, a `title` property for the application name and a `hero` property
@ -66,7 +64,7 @@ include ../../../../_includes/_util-fns
:markdown
Now we update the template in the `@Component` decoration with data bindings to these new properties.
code-example(format="linenums").
code-example(format="").
template: '&lt;h1>{{title}}&lt/h1>&lth2>{{hero}} details!&lt/h2>'
:markdown
The browser should refresh and display our title and hero.
@ -75,7 +73,7 @@ include ../../../../_includes/_util-fns
This is the "interpolation" form of one-way data binding;
we can learn more about interpolation in the [Displaying Data chapter](displaying-data).
## Hero Object
### Hero object
At the moment, our hero is just a name. Our hero needs more properties.
Let's convert the `hero` from a literal string to a class.
@ -103,12 +101,12 @@ include ../../../../_includes/_util-fns
Because we changed the hero from a string to an object,
we update the binding in the template to refer to the heros `name` property.
code-example(format="linenums").
code-example(format="").
template: '&lt;h1>{{title}}&lt/h1>&lth2>{{hero.name}} details!&lt/h2>'
:markdown
The browser refreshes and continues to display our heros name.
## **Adding more HTML**
### Adding more HTML
Displaying a name is good, but we want to see all of our heros properties.
Well add a `<div>` for our heros `id` property and another `<div>` for our heros `name`.
@ -117,7 +115,7 @@ include ../../../../_includes/_util-fns
:markdown
Uh oh, our template string is getting long. We better take care of that to avoid the risk of making a typo in the template.
### Multi-line Template Strings
### Multi-line template strings
We could make a more readable template with string concatenation
but that gets ugly fast, it is harder to read, and
@ -147,9 +145,8 @@ include ../../../../_includes/_util-fns
is part of a single template string.
.l-main-section
:markdown
# Editing Our Hero
## Editing Our Hero
We want to be able to edit the hero name in a textbox.
@ -161,7 +158,7 @@ include ../../../../_includes/_util-fns
&ltdiv>&ltlabel>id: &lt/label>{{hero.id}}&lt/div>
&ltdiv>
&ltlabel>name: &lt/label>
&ltdiv>&ltinput value="{{hero.name}}" placeholder="name">&lt/input>&lt/div>
&ltdiv>&ltinput value="{{hero.name}}" placeholder="name">&lt/div>
&lt/div>
`
:markdown
@ -171,7 +168,7 @@ include ../../../../_includes/_util-fns
is not reflected in the `<h2>`. We won't get the desired behavior
with a one-way binding to `<input>`.
## Two-Way Binding
### Two-Way Binding
We intend to display the name of the hero in the `<input>`, change it,
and see those changes wherever we bind to the heros name.
@ -179,22 +176,22 @@ include ../../../../_includes/_util-fns
Lets update the template to use the **`ng-model`** built-in directive for two-way binding.
.alert.is-helpful
.l-sub-section
:markdown
Learn more about `ng-model` in the [Template Syntax](../guide/template-syntax.html#ng-model)
:markdown
Replace the `<input>` with the following HTML
```
<input [(ng-model)]="hero.name" placeholder="name"></input>
```
code-example(language="html").
&lt;input [(ng-model)]="hero.name" placeholder="name">
:markdown
Unfortunately, that change broke our application and we're no longer displaying the hero in the browser.
Lets fix that next.
.l-main-section
:markdown
# Declaring Template Directives
## Declaring Template Directives
We added the `ng-model` directive but we didn't tell Angular about it.
A component must disclose every directive that appears in its template.
@ -218,10 +215,10 @@ include ../../../../_includes/_util-fns
Unfortunately when we view the app in the browser we still have an error:
```
*EXCEPTION: No value accessor for ' ' in [null]*
```
code-example(language="html").
EXCEPTION: No value accessor for ' ' in [null]
:markdown
Apparently declaring the `NgModel` is not quite enough.
## Declare Multiple Form Directives
@ -253,7 +250,7 @@ include ../../../../_includes/_util-fns
The browser refreshes. We see our hero again. We can edit the heros name and
see the changes reflected immediately in the `<h2>`.
### Bundled Directives
### Bundled directives
Angular bundled the Form-related directives together in a convenient `FORM_DIRECTIVES` array.
That's all we need to remember to light up our template.
@ -261,7 +258,9 @@ include ../../../../_includes/_util-fns
We too can bundle a collection of directives in an array, give it a catchy name,
and plug that array into the `directives` property.
# The Road Weve Travelled
.l-main-section
:markdown
## The Road Weve Travelled
Lets take stock of what weve built.
* Our Tour of Heroes uses the double curly braces of interpolation (a form of one-way data binding)
@ -290,7 +289,7 @@ include ../../../../_includes/_util-fns
&ltdiv>&ltlabel>id: &lt/label>{{hero.id}}&lt/div>
&ltdiv>
&ltlabel>name: &lt/label>
&ltdiv>&ltinput value="{{hero.name}}" placeholder="name">&lt/input>&lt/div>
&ltdiv>&ltinput [(ng-model)]="hero.name" placeholder="name">&lt/div>
&lt/div>
`,
directives: [FORM_DIRECTIVES]
@ -305,8 +304,9 @@ include ../../../../_includes/_util-fns
bootstrap(AppComponent);
.l-main-section
:markdown
# The Road Ahead
## The Road Ahead
Our Tour of Heroes only displays one hero and we really want to display a list of heroes.
We also want to allow the user to select a hero and display their details.
Well learn more about how to retrieve lists, bind them to the

View File

@ -13,7 +13,7 @@ include ../../../../_includes/_util-fns
.l-main-section
:markdown
# Where We Left Off
## Where We Left Off
Before we continue with Part 2 of the Tour of Heroes,
lets verify we have the following structure after [Part 1](./toh-pt1.html).
If not, well need to go back to Part 1 and figure out what we missed.
@ -28,7 +28,7 @@ include ../../../../_includes/_util-fns
| └── tsconfig.json
└── package.json
:markdown
## Keep the App Running
### Keep the app running
Start the TypeScript compiler and have it watch for changes in one terminal window by typing
pre.prettyprint.lang-bash
@ -43,8 +43,10 @@ include ../../../../_includes/_util-fns
:markdown
This will keep the application running while we continue to build the Tour of Heroes.
# Displaying Our Heroes
## Creating Heroes
.l-main-section
:markdown
## Displaying Our Heroes
### Creating heroes
Lets create an array of ten heroes at the bottom of `app.ts`.
```
var HEROES: Hero[] = [
@ -65,20 +67,20 @@ include ../../../../_includes/_util-fns
We aspire to get this list of heroes from a web service, but lets take small steps
on this road and start by displaying these mock heroes in the browser.
## Exposing Heroes
### Exposing heroes
Lets create a public property in `AppComponent` that exposes the heroes for binding.
```
public heroes = HEROES;
```
We did not have to define the `heroes` type. TypeScript can infer it from the `HEROES` array.
.alert.is-helpful
.l-sub-section
:markdown
We could have defined the heroes list here in this component class.
But we know that well get the heroes from a data service.
Because know where we are heading, it makes sense to separate the hero data
Because we know where we are heading, it makes sense to separate the hero data
from the class implementation from the start.
:markdown
## Displaying Heroes in a Template
### Displaying heroes in a template
Our component has`heroes`. Lets create an unordered list in our template to display them.
Well insert the following chunk of HTML below the title and above the hero details.
```
@ -91,7 +93,7 @@ include ../../../../_includes/_util-fns
```
Now we have a template that we can fill with our heroes.
## Iterating Over Our Heroes with `ng-for`
### Listing heroes with ng-for
We want to bind the array of `heroes` in our component to our template, iterate over them,
and display them individually.
@ -101,14 +103,13 @@ include ../../../../_includes/_util-fns
```
<li *ng-for="#hero of heroes">
```
.alert.is-important
.alert.is-critical
:markdown
Do not neglect the leading asterisk (`*`) in front of `ng-for`!
The leading asterisk (`*`) in front of `ng-for` is a critical part of this syntax.
Although it is not part of the `ng-for` directive name,
it is a critical part of the syntax in this usage of `ng-for`.
.l-sub-section
:markdown
The (`*`) prefix to the `ng-for` indicates that the `<li>` element and its children
The (`*`) prefix to `ng-for` indicates that the `<li>` element and its children
constitute a master template.
The `ng-for` directive iterates over the `heroes` array returned by the `AppComponent.heroes` property
@ -121,27 +122,29 @@ include ../../../../_includes/_util-fns
The `#` prefix before "hero" identifies the `hero` as a local template variable.
We can reference this variable within the template to access a heros properties.
.alert.is-helpful
:markdown
Learn more about `ng-for` and local template variables in the
[Template Syntax](../guide/template-syntax.html#ng-for) chapter.
:markdown
With this background in mind, we now insert some content between the `<li>` tags
that uses the `hero` template variable to display the heros properties.
code-example.
code-example(format="linenums" language="html").
&lt;li *ng-for="#hero of heroes">
&lt;span class="badge">{{hero.id}}&lt;/span> {{hero.name}}&lt;/a>
&lt;/li>
:markdown
## Declaring NgFor
### Declaring ng-for
When we view the running app in the browser we see nothing … no heroes.
We open the developer tools and see an error in the console.
```
code-example(language="html" ).
EXCEPTION:
Can't bind to 'ngForOf' since it isn't a known property of the '<template>' element and
Can't bind to 'ngForOf' since it isn't a known property of the '&lt;template>' element and
there are no matching directives with a corresponding property
```
:markdown
Thankfully we have a clear error message that indicates where we went wrong.
We used `ng-for` in the template but we didnt tell the component about it.
From Angular's perspective, `ng-for` is a meaningless attribute.
@ -159,14 +162,14 @@ include ../../../../_includes/_util-fns
```
After the browser refreshes, we see a list of heroes!
## Styling Our Heroes
### Styling our heroes
Our list of heroes looks pretty bland.
We want to make it visually obvious to a user which hero we are hovering over and which hero is selected.
Lets add some styles to our component by setting the `styles` property on the `@Component` decorator
to the following CSS classes:
```
styles: `
styles:[`
.heroes {list-style-type: none; margin-left: 1em; padding: 0; width: 10em;}
.heroes li { cursor: pointer; position: relative; left: 0; transition: all 0.2s ease; }
.heroes li:hover {color: #369; background-color: #EEE; left: .2em;}
@ -181,7 +184,7 @@ include ../../../../_includes/_util-fns
top: -1px;
}
.selected { background-color: #EEE; color: #369; }
`
`],
```
Notice that we again use the back-tick notation for multi-line strings.
@ -205,7 +208,7 @@ include ../../../../_includes/_util-fns
.l-main-section
:markdown
# Selecting a Hero
## Selecting a Hero
We have a list of heroes and we have a single hero displayed in our app.
The list and the single hero are not connected in any way.
We want the user to select a hero from our list, and have the selected hero appear in the details view.
@ -214,7 +217,7 @@ include ../../../../_includes/_util-fns
Lets connect the master to the detail through a `selectedHero` component property bound to a click event.
## Click Event
### Click event
We modify the `<li>` by inserting an Angular event binding to its click event.
code-example(format="linenums").
@ -230,11 +233,11 @@ include ../../../../_includes/_util-fns
The expression to the right of the equal sign calls the `AppComponent` method, `onSelect()`,
passing the local template variable `hero` as an argument.
Thats the same `hero` variable we defined previously in the `ng-for`.
.alert.is-helpful
.l-sub-section
:markdown
Learn more about Event Binding in the [Templating Syntax](../guide/template-syntax.html#event-binding) chapter.
:markdown
## Add the click handler
### Add the click handler
Our event binding refers to an `onSelect` method that doesnt exist yet.
Well add that method to our component now.
@ -242,7 +245,7 @@ include ../../../../_includes/_util-fns
Our component doesnt have a “selected hero” yet either. Well start there.
### Expose the Selected Hero
### Expose the selected hero
We no longer need the static `hero` property of the `AppComponent`.
**Replace** it with this simple `selectedHero` property:
```
@ -269,14 +272,16 @@ include ../../../../_includes/_util-fns
&lt;/div>
:markdown
### Hide the empty detail with NgIf
### Hide the empty detail with ng-if
When our app loads we see a list of heroes, but a hero is not selected.
The `selectedHero` is `undefined`.
Thats why we'll see the following error in the browsers console:
```
code-example(language="html").
EXCEPTION: TypeError: Cannot read property 'name' of undefined in [null]
```
:markdown
Remember that we are displaying `selectedHero.name` in the template.
This name property does not exist because `selectedHero`itself is undefined.
@ -294,18 +299,17 @@ include ../../../../_includes/_util-fns
&lt;input [(ng-model)]="selectedHero.name" placeholder="name">&lt;/input>
&lt;/div>
&lt;/div>
.alert.is-important
.alert.is-critical
:markdown
Do not neglect the leading asterisk (`*`) in front of `ng-if`.
It's a critical part of the syntax in this usage of `ng-if`.
Remember that the leading asterisk (`*`) in front of `ng-if` is
a critical part of this syntax.
:markdown
When there is no `selectedHero`, the `ng-if` directive removes the hero detail HTML from the DOM.
There will no hero detail elements and no bindings to worry about.
When the user picks a hero, `selectedHero` becomes "truthy" and
`ng-if` puts the hero detail content into the DOM and evaluates the nested bindings.
.alert.is-helpful
.l-sub-section
:markdown
`ng-if` and `ng-for` are called “structural directives” because they can change the
structure of portions of the DOM.
@ -333,7 +337,7 @@ include ../../../../_includes/_util-fns
When we click on a hero in the list, the selected hero displays in the hero details.
Everything is working as we expect.
### Styling the Selection
### Styling the selection
We see the selected hero in the details area below but we cant quickly locate that hero in the list above.
We can fix that by applying the `selected` CSS class to the appropriate `<li>` in the master list.
@ -356,7 +360,7 @@ include ../../../../_includes/_util-fns
```
What do we do with this method and its peculiar result?
### NgClass
### ng-class
Well add the `ng-class`built-in directive to the `<li>` element in our template and bind it to `getSelectedClass`.
Its no coincidence that the value returned by `getSelectedClass` is exactly what the `ng-class` requires
to add or remove the `selected` class to each heros display.
@ -367,20 +371,16 @@ include ../../../../_includes/_util-fns
&lt;span class="badge">{{hero.id}}&lt;/span> {{hero.name}}&lt;/a>
&lt;/li>
.alert.is-helpful
:markdown
Learn more about `ng-class` in the
[Template Syntax](../guide/template-syntax.html#ng-class) chapter
:markdown
Notice in the template that the `ng-class` name is surrounded in square brackets (`[]`).
This is the syntax for a Property Binding, a binding in which data flows one way
from the data source (the `getSelectedClass`) to a property of the `ng-class` directive.
.alert.is-helpful
.l-sub-section
:markdown
Learn more about about one-way property data binding in the
[Template Syntax](../guide/template-syntax.html#property-binding) chapter
Learn more about [ng-class](../guide/template-syntax.html#ng-class)
and [Property Binding](../guide/template-syntax.html#property-binding)
in the Template Syntax chapter
:markdown
We've added yet another new directive to our template that we have to import and declare
in the components `directives` array as weve done twice before.
@ -428,14 +428,15 @@ include ../../../../_includes/_util-fns
.l-main-section
:markdown
# The Road Weve Travelled
## The Road Weve Travelled
Heres what we achieved in this chapter:
* Our Tour of Heroes now displays a list of selectable heroes
* We added the ability to select a hero and show the heros details
* We learned how to use the built-in directives `ng-if`, `ng-for` and `ng-class` in a components template
## The Road Ahead
### The Road Ahead
Our Tour of Heroes has grown, but its far from complete.
We want to get data from an asynchronous source using promises, use shared services, and create reusable components.
Well learn more about these tasks in the coming tutorial chapters.