Tutorial: 8 - More Templating

In this step, you will implement the phone details view, which is displayed when a user clicks on a phone in the phone list.

Now 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 will use $xhr to fetch our data, and we'll flesh out the phone-details.html view template.

The most important changes are listed below. You can see the full diff on GitHub:

Data

In addition to phones.json, the app/phones/ directory also contains one json file for each phone:

app/phones/nexus-s.json: (sample snippet)

{
  "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'll show this data in the phone detail view.

Controller

We'll expand the PhoneDetailCtrl by using the $xhr service to fetch the json files. This works the same way as the phone list controller.

app/js/controller.js:

function PhoneDetailCtrl($xhr) {
  var self = this;


  $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) {
    self.phone = response;
  });
}


//PhoneDetailCtrl.$inject = ['$xhr'];

To construct the URL for the HTTP request, we use params.phoneId extracted from the current route in the PhoneCatCtrl controller.

Template

The TBD placeholder line has been replaced with lists and bindings that comprise the phone details. Note where we use the angular {{expression}} markup and ng:repeaters to project phone data from our model into the view.

app/partials/phone-details.html:

<img ng:src="{{phone.images[0]}}" class="phone"/>


<h1>{{phone.name}}</h1>


<p>{{phone.description}}</p>


<ul class="phone-thumbs">
  <li ng:repeat="img in 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 phone.availability">{{availability}}</dd>
    </dl>
  </li>
    ...
  </li>
    <span>Additional Features</span>
    <dd>{{phone.additionalFeatures}}</dd>
  </li>
</ul>

Test

We wrote a new unit test that is similar to the one we wrote for the PhoneListCtrl controller in step 5.

test/unit/controllerSpec.js:

...
    it('should fetch phone detail', function() {
      scope.params = {phoneId:'xyz'};
      $browser.xhr.expectGET('phones/xyz.json').respond({name:'phone xyz'});
      ctrl = scope.$new(PhoneDetailCtrl);


      expect(ctrl.phone).toBeUndefined();
      $browser.xhr.flush();


      expect(ctrl.phone).toEqual({name:'phone xyz'});
    });
...

To run the unit tests, execute the ./scripts/test.sh script and you should see the following output.

Chrome: Runner reset.
...
Total 3 tests (Passed: 3; Fails: 0; Errors: 0) (5.00 ms)
  Chrome 11.0.696.57 Mac OS: Run 3 tests (Passed: 3; Fails: 0; Errors 0) (5.00 ms)

We also added a new end-to-end test that navigates to the Nexus S detail page and verifies that the heading on the page is "Nexus S".

test/e2e/scenarios.js:

...
  describe('Phone detail view', function() {


    beforeEach(function() {
      browser().navigateTo('../../app/index.html#/phones/nexus-s');
    });




    it('should display nexus-s page', function() {
      expect(binding('phone.name')).toBe('Nexus S');
    });
  });
...

You can now refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running on angular's server.

Experiments

Summary

Now that the phone details view is in place, proceed to step 9 to learn how to write your own custom display filter.