docs/content/tutorial/step_10.ngdoc
@ngdoc tutorial
@name 10 - More Templating
@step 10
@description
<ul doc-tutorial-nav="10"></ul>
In this step, we will implement the phone details view, which is displayed when a user clicks on a
phone in the phone list.
* When you click on a phone on the list, the phone details page with phone-specific information is
displayed.
To implement the phone details view we are going to use {@link ng.$http $http} to fetch our data,
and then flesh out the `phoneDetail` component's template.
<div doc-tutorial-reset="10"></div>
## Data
In addition to `phones.json`, the `app/phones/` directory also contains one JSON file for each
phone:
<br />
**`app/phones/nexus-s.json`:** (sample snippet)
```json
{
"additionalFeatures": "Contour Display, Near Field Communications (NFC), ...",
"android": {
"os": "Android 2.3",
"ui": "Android"
},
...
"images": [
"img/phones/nexus-s.0.jpg",
"img/phones/nexus-s.1.jpg",
"img/phones/nexus-s.2.jpg",
"img/phones/nexus-s.3.jpg"
],
"storage": {
"flash": "16384MB",
"ram": "512MB"
}
}
```
Each of these files describes various properties of the phone using the same data structure. We will
show this data in the phone details view.
## Component Controller
We will expand the `phoneDetail` component's controller by using the `$http` service to fetch the
appropriate JSON files. This works the same way as the `phoneList` component's controller.
<br />
**`app/phone-detail/phone-detail.component.js`:**
```js
angular.
module('phoneDetail').
component('phoneDetail', {
templateUrl: 'phone-detail/phone-detail.template.html',
controller: ['$http', '$routeParams',
function PhoneDetailController($http, $routeParams) {
var self = this;
$http.get('phones/' + $routeParams.phoneId + '.json').then(function(response) {
self.phone = response.data;
});
}
]
});
```
To construct the URL for the HTTP request, we use `$routeParams.phoneId`, which is extracted from
the current route by the `$route` service.
## Component Template
The inline, TBD placeholder template has been replaced with a full blown external template,
including lists and bindings that comprise the phone details. Note how we use the AngularJS
`{{expression}}` markup and `ngRepeat` to project phone data from our model into the view.
<br />
**`app/phone-detail/phone-detail.template.html`:**
```html
<img ng-src="{{$ctrl.phone.images[0]}}" class="phone" />
<h1>{{$ctrl.phone.name}}</h1>
<p>{{$ctrl.phone.description}}</p>
<ul class="phone-thumbs">
<li ng-repeat="img in $ctrl.phone.images">
<img ng-src="{{img}}" />
</li>
</ul>
<ul class="specs">
<li>
<span>Availability and Networks</span>
<dl>
<dt>Availability</dt>
<dd ng-repeat="availability in $ctrl.phone.availability">{{availability}}</dd>
</dl>
</li>
...
<li>
<span>Additional Features</span>
<dd>{{$ctrl.phone.additionalFeatures}}</dd>
</li>
</ul>
```
<img class="diagram" src="img/tutorial/tutorial_10.png">
## Testing
We wrote a new unit test that is similar to the one we wrote for the `phoneList` component's
controller in {@link step_07#testing step 7}.
<br />
**`app/phone-detail/phone-detail.component.spec.js`:**
```js
describe('phoneDetail', function() {
// Load the module that contains the `phoneDetail` component before each test
beforeEach(module('phoneDetail'));
// Test the controller
describe('PhoneDetailController', function() {
var $httpBackend, ctrl;
beforeEach(inject(function($componentController, _$httpBackend_, $routeParams) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/xyz.json').respond({name: 'phone xyz'});
$routeParams.phoneId = 'xyz';
ctrl = $componentController('phoneDetail');
}));
it('should fetch the phone details', function() {
expect(ctrl.phone).toBeUndefined();
$httpBackend.flush();
expect(ctrl.phone).toEqual({name: 'phone xyz'});
});
});
});
```
You should now see the following output in the Karma tab:
```
Chrome 49.0: Executed 3 of 3 SUCCESS (0.159 secs / 0.136 secs)
```
We also added a new E2E test that navigates to the 'Nexus S' details page and verifies that the
heading on the page is "Nexus S".
<br />
**`e2e-tests/scenarios.js`**
```js
...
describe('View: Phone detail', function() {
beforeEach(function() {
browser.get('index.html#!/phones/nexus-s');
});
it('should display the `nexus-s` page', function() {
expect(element(by.binding('$ctrl.phone.name')).getText()).toBe('Nexus S');
});
});
...
```
You can run the tests with `npm run protractor`.
## Experiments
<div></div>
* Using [Protractor's API][protractor-docs], write a test that verifies that we display 4 thumbnail
images on the 'Nexus S' details page.
## Summary
Now that the phone details view is in place, proceed to {@link step_11 step 11} to learn how to
write your own custom display filter.
<ul doc-tutorial-nav="10"></ul>
[protractor-docs]: https://angular.github.io/protractor/#/api