Reusable components with AngularJS

Recently I came across AngularJS – a javascript framework to boost productivity of front-end development.

They have this awesome tutorial where you can download code from Github and they guide you through a sequence of steps. Very well done.

When I finished it I was missing something though. I still didn’t know how to create reusable components using Angular.
After a little bit of research, I found out that it’s not missing from Angular, it’s just missing from the tutorial.

So I made two pull requests (step-12 and step-13) to their repo. I hope they add that to their tutorial (well, they would probably need to improve it a bit first).
I’ll try to explain here how the code works:
I’m assuming you have already tried the official tutorial yourself, in other words, you have cloned their repo and played a bit with it.

You can go ahead and clone QMagico’s forked angular-phonecat repo, and checkout into branch step-12.
The open http://localhost:8000/app/using_components.html
You’ll see two sets of pictures, with their thumbnails that you can click to view a larger image.

Here’s the code for app/using_components.html:

<!doctype html>
<html lang="en" ng-app="using_components" ng-controller="UsingComponentsCtrl">
 <meta charset="utf-8">
 <title>Google Phone Gallery</title>
 <link rel="stylesheet" href="css/app.css">
 <link rel="stylesheet" href="css/bootstrap.css">
 <script src="lib/angular/angular.js"></script>
 <script src="js/using_components.js"></script>
 <script src="js/services.js"></script>
 <script src="lib/angular/angular-resource.js"></script>

<phoneimages phone="phone1"></phoneimages>

 Who bought this, also bought

<phoneimages phone="phone2"></phoneimages>


That <phoneimages> tag is the custom component.
The attributes phone1 and phone2 are two variables defined in the controller $scope, in js/using_components.js.

Here’s the code for it:

'use strict';

/* App Module */
angular.module('using_components', ['phonecatServices'])

function UsingComponentsCtrl($scope, Phone) {
  $scope.phone1 = Phone.get({phoneId: 'nexus-s'});
  $scope.phone2 = Phone.get({phoneId: 'motorola-xoom'});

In this file we’re defining both the module (which is called “using_components”) and the controller.
In other words, the file using_components.js is playing the role of app.js and controllers.js in the previous steps.

Our module depends on phonecatServices, which is defined in js/services.js

Here’s the code:

'use strict';

/* Services */

angular.module('phonecatServices', ['ngResource']).
    factory('Phone', function($resource){
  return $resource('phones/:phoneId.json', {}, {
    query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
}).directive('phoneimages', function() {
    return {
      restrict: 'E',
      templateUrl: 'partials/phoneimg.html',
      controller: function ($scope) {
        $scope.imgindex = 0

        $scope.setIndex = function(index) {
          $scope.imgindex = index;

There you can see the already familiar Phone service factory, and we also have something new: The phoneimages directive.
That’s our custom component. This is what angular will use to build our component when it encounters the <phoneimages> tag.
What the code means:

  • restrict: ‘E’ – means that our component is a html element
  • scope:{phone:’=phone’} – means that our component $scope will have a ‘phone’ variable, binded to whatever is passed in the ‘phone’ attribute
  • templateUrl – The html code that Angular will use to render this component
  • controller: function($scope){…} – This component javascript behaviour

The last piece of code missing is the html template: partials/phoneimg.html:

    <img ng-src="{{phone.images[imgindex]}}" class="phone">
    <ul class="phone-thumbs">
      <li ng-repeat="img in phone.images" ng-click="setIndex($index)">
        <img ng-src="{{img}}">

There you have it. A “images with clickable thumbnails” component, where the click-to-enlarge behaviour is encapsulated away from the client.

This is a very powerful feature of AngularJS.

Now that you understand Angular components, take a look at this short video to learn how to use transclusion to nest components: