In this step, you will add a clickable phone image swapper to the phone details page.
The phone details view displays one large image of the current phone and several smaller thumbnail images. It would be great if we could replace the large image with any of the thumbnails just by clicking on the desired thumbnail image. Let's have a look at how we can do this with angular.
The most important changes are listed below. You can see the full diff on GitHub:
app/js/controllers.js
:
... function PhoneDetailCtrl($xhr) { var self = this; $xhr('GET', 'phones/' + self.params.phoneId + '.json', function(code, response) { self.phone = response; self.mainImageUrl = response.images[0]; }); self.setImage = function(imageUrl) { self.mainImageUrl = imageUrl; } } //PhoneDetailCtrl.$inject = ['$xhr'];
In the PhoneDetailCtrl
controller, we created the mainImageUrl
model property and set its
default value to the first phone image url.
We also created a setImage
controller method to change the value of mainImageUrl
.
app/partials/phone-detail.html
:
<img ng:src="{{mainImageUrl}}" class="phone"/> ... <ul class="phone-thumbs"> <li ng:repeat="img in phone.images"> <img ng:src="{{img}}" ng:click="setImage(img)"> </li> </ul> ...
We bound the ng:src
attribute of the large image to the mainImageUrl
property.
We also registered an
handler with thumbnail
images. When a user clicks on one of the thumbnail images, the handler will use the ng:click
setImage
controller method to change the value of the mainImageUrl
property to the url of the thumbnail
image.
To verify this new feature, we added two end-to-end tests. One verifies that the main image is set to the first phone image by default. The second test clicks on several thumbnail images and verifies that the main image changed appropriately.
test/e2e/scenarios.js
:
... describe('Phone detail view', function() { beforeEach(function() { browser().navigateTo('../../app/index.html#/phones/nexus-s'); }); it('should display the first phone image as the main phone image', function() { expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg'); }); it('should swap main image if a thumbnail image is clicked on', function() { element('.phone-thumbs li:nth-child(3) img').click(); expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.2.jpg'); element('.phone-thumbs li:nth-child(1) img').click(); expect(element('img.phone').attr('src')).toBe('img/phones/nexus-s.0.jpg'); }); }); });
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.
Let's add a new controller method to PhoneCatCtrl
:
this.hello = function(name) {
alert('Hello ' + (name || 'world') + '!');
}
and add:
<button ng:click="hello('Elmo')">Hello</button>
to the index.html
template.
The controller methods are inherited between controllers/scopes, so you can use the same snippet
in the phone-list.html
template as well.
Move the hello
method from PhoneCatCtrl
to PhoneListCtrl
and you'll see that the button
declared in index.html
will stop working, while the one declared in the phone-list.html
template remains operational.
With the phone image swapper in place, we're ready for step 11 (the last step!) to learn an even better way to fetch data.