<!DOCTYPE html>
<html ng-app="DirectoryDemo">

  <head>
    <link data-require="bootstrap-css@*" data-semver="3.3.1" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
    <script data-require="angular.js@2.0.0-alpha.31" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <div class="container" ng-controller="MainController">
      <h1>Passing data with services in AngularJS.</h1>
      
      <p>
        The purpose of this demo is to show how to pass messages 
        between controllers, directives and services in angular.
      </p>
      
      <p>
        The advantages of this approach are:
      </p>
      
      <ul>
        <li>
          Thin controllers as all business logic and data persistence 
          is handled by the service.
        </li>
        
        <li>
          Directives have access to the full data set rather than 
          what is passed through scope.
        </li>
        
        <li>
          There is only ever one copy of the data throughout the 
          app.
        </li>
      </ul>
      
      <p>
        Open <code>script.js</code> to see the fully commented 
        code. To see the code in action, first add a person using 
        the form below and watch it automatically appear in the 
        DirectoryList directive, then click the 
        <span class="small text-muted">&times;</span>
        next to a persons name to remove them from the directory.
      </p>
      
      <hr>
      
      <h2>Add a Person</h2>
      
      <form class="form-horizontal">
        <div class="form-group">
          <label for="firstname" class="col-xs-2 control-label">Firstname:</label>
          
          <div class="col-xs-10">
            <input type="text" id="firstname" class="form-control" ng-model="person.firstname" placeholder="Firstname" />
          </div>
        </div>
        
        <div class="form-group">
          <label for="lastname" class="col-xs-2 control-label">Lastname:</label>
          
          <div class="col-xs-10">
            <input type="text" id="lastname" class="form-control" ng-model="person.lastname" placeholder="Lastname" />
          </div>
        </div>
        
        <div class="form-group">
          <div class="col-xs-offset-2 col-xs-10">
            <button class="btn btn-primary" ng-click="addPerson(person)">Add Person</button>
            <button class="btn btn-danger" ng-click="reset()">Reset</button>
          </div>
        </div>
      </form>
      
      <hr />
      
      <h2>Directory</h2>
      <directory-list></directory-list>
    </div>
  </body>

</html>
(function (ng) {
  'use strict';

  // intialize module
  var app = ng.module('DirectoryDemo', []);
  
  // define components
  app.controller('MainController', MainController);
  app.directive('directoryList', DirectoryList);
  app.service('Directory', Directory);
  app.factory('Person', Person);
  
  /**
   * MainController
   * 
   * The main controller controls creating people
   * and adding them to the directory. It can also
   * reset the directory contents.
   * 
   * The controller is kept light as all logic is handled 
   * by the Directory service.
   */
  function MainController ($scope, Directory) {
    $scope.person = {};
    $scope.addPerson = Directory.addPerson;
    $scope.reset = Directory.reset;
  }
  
  /**
   * DirectoryList
   * 
   * This directive lists the full name of all the people
   * in the directory. Clicking a name removes it from the
   * directory.
   * 
   * The information is retrieved directly from the Directory 
   * service. Nothing is passed through scope.
   * 
   * The controller is kept light as all logic is handled 
   * by the service.
   */
  function DirectoryList (Directory) {
    var DirectoryListDirective = {
      scope: {},
      restrict: 'EA',
      template: '<table class="table table-striped table-hover table-bordered">' +
                  '<tr ng-repeat="person in people track by $index">' +
                    '<td>' +
                      '{{person.getFullName()}}' +
                      '<div class="close" ng-click="removePerson(person)">&times;</div>' +
                    '</td>' +
                  '</tr>' +
                '</table>',
      
      controller: DirectoryListController
    };
    
    function DirectoryListController ($scope, $element, $attrs) {
      $scope.people = Directory.people;
      $scope.removePerson = Directory.removePerson;
    }
    
    return (DirectoryListDirective);
  }
  
  /**
   * Directory
   * 
   * This service holds an array of people. It also
   * exposes some basic methods to control the array.
   * 
   * As this is a service, variables and methods are
   * instantiated directly on to 'this'.
   */
  function Directory (Person, $q, $log) {
    var self = this;
    
    // array of Person objects
    this.people = [];
    
    // define methods
    this.addPerson = addPerson;
    this.removePerson = removePerson;
    this.reset = reset;
    
    /**
     * The addPerson method first attempts to find the person in 
     * the directory. If the person is not found, add it.
     */
    function addPerson (person) {
      _find(person.firstname, person.lastname)
        .then(_onPersonFound, _onPersonNotFound);
        
      function _onPersonFound () {
        $log.info(person.firstname + ' ' + person.lastname + ' already exists');
      }
      
      function _onPersonNotFound () {
        if (person.firstname && person.lastname) {
          self.people.push(new Person(person.firstname, person.lastname)); 
        }
      }
    }
    
    /**
     * The removePerson method first attempts to find the person in 
     * the directory. If the person is found, remove it.
     */
    function removePerson (person) {
      _find(person.firstname, person.lastname)
        .then(_onPersonFound, _onPersonNotFound);
      
      function _onPersonFound (foundPerson) {
        self.people.splice(foundPerson.index, 1);
      }
      
      function _onPersonNotFound (err) {
        $log.info('Directory Error', err);
      }
    }
    
    /**
     * The reset method emptys the directory
     */
    function reset () {
      self.people.length = 0;
    }
    
    /**
     * This private method attempts to find a person in
     * the directory, given a first name and last name.
     * 
     * It returns a promise containing either the person 
     * object and its index in the people array, or a 
     * message if no object is found.
     */
    function _find (firstname, lastname) {
      var d = $q.defer(),
          found = false;
      
      // loop through the people array and compare
      angular.forEach(self.people, function (person, index) {
        if (person.firstname === firstname && person.lastname === lastname) {
          found = true;
          d.resolve({ person: person, index: index });
        }
      });
      
      // if not found, set error message
      if (!found) {
        d.reject('Person not found');
      }
      
      return d.promise;
    }
  }
  
  /**
   * Person
   * 
   * This object holds stores properties and exposes
   * basic methods about a person.
   * 
   * As it is instantiated as a factory, we create a new 
   * object each time using the following syntax:
   * 
   * var john = new Person('John', 'Smith');
   */
  function Person () {
    function PersonClass (firstname, lastname) {
      var self = this;
      
      // define properties
      this.firstname = firstname;
      this.lastname = lastname;
      
      // define methods
      this.getFullName = getFullName;
      
      /**
       * Return persons full name
       */
      function getFullName () {
        return self.firstname + ' ' + self.lastname;
      }
    }
    
    return (PersonClass);
  }
})(angular);
# Passing data with services in AngularJS.

The purpose of this demo is to show how to pass messages between controllers, directives and services in angular.

The advantages of this approach are:

- Thin controllers as all business logic and data persistence is handled by 
- the service.
- Directives have access to the full data set rather than what is passed 
- through scope.
- There is only ever one copy of the data throughout the app.
- Open script.js to see the fully commented code. To see the code in action, 
- first add a person using the form below and watch it automatically appear 
- in the DirectoryList directive, then click the × next to a persons name to 
- remove them from the directory.

Open `script.js` to see the fully commented code. To see the code in action, 
first add a person using the form and watch it automatically appear in the 
DirectoryList directive, then click the × next to a persons name to remove 
them from the directory.