<!DOCTYPE html>
<html>

<head>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-route.js"></script>
  <script src="model.js"></script>
  <script src="model-service.js"></script>
  <script src="app.js"></script>
  <script src="app-controllers.js"></script>
  <script src="company-factory.js"></script>
  <link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
</head>

<body ng-app="ModelDemo" class="container">

  <h1 class="page-header">Company Utility</h1>
  <div>
    <a href="#/list" class="btn btn-default">List</a>
    <a href="#/crm" class="btn btn-primary">CRM</a>
    <a ng-click="listPlus = !listPlus" class="btn btn-info">Toggle List Plus</a>
  </div>
  <hr />
  <div ng-view></div>
</body>

</html>
# Demonstration of a model layer in angular
var baseModel = angular.module('baseModel', []);

// company prototype
baseModel.constant('BaseCompany', function() {
  this.count = 0;
  this.add = function() {
    return this.count++;
  };
  this.subtract = function() {
    return this.count--;
  };
});

baseModel.service('Company', function() {
  return function(data) {
    angular.extend(this, data);
  }
});

// company mixin with CRM functionality
baseModel.constant('CompanyCRMVisitor', {
  CRM: true,
  addToCRM: function() {
    this.inCRM = true;
  },
  removeFromCRM: function() {
    this.inCRM = false;
  }
});

// company mixin with List plus functionality
baseModel.service('CompanyPlusVisitor', function($http, $rootScope) {
  this.plus = true;
  this.getText = function(company) {
    $http.get('http://baconipsum.com/api/?type=meat-and-filler')
      .then(function(resp) {
        company.text = resp.data.pop();
      });
  };

  this.saveCompany = function(company) {
    $http.post('http://baconipsum.com/api/?type=meat-and-filler')
      .then(function() {
        company.saved = true;
      });
  };
});
/*global angular: false, alert: false*/

var app = angular.module('ModelDemo', ['baseModel', 'ngRoute']);

app.config(function($routeProvider) {
  $routeProvider
    .when('/list', {
      templateUrl: 'list.html',
      controller: 'ListCtrl'
    })
    .when('/crm', {
      templateUrl: 'list-crm.html',
      controller: 'CrmListCtrl'
    })
    .when('/company/:duns', {
      templateUrl: 'company-detail.html',
      controller: 'CompanyDetailCtrl',
    })
    .otherwise({
      redirectTo: '/list'
    });
});

// set global decorator
app.run(function(companyFactory, CompanyPlusVisitor, $rootScope, $route) {
  $rootScope.$watch('listPlus', function (newVal) {
    if (newVal) {
      companyFactory.setGlobalVisitor(CompanyPlusVisitor);
    }
    else {
      companyFactory.setGlobalVisitor({});
    }
    $route.reload();
  })
});
[  
      {  
         "companyId":11166000000000,
         "name":"FOOCO, INC.",
         "duns":"001287762",
         "webAddress":"http://www.www.fooico.com",
         "locationType":"Headquarters",
         "address":{  
            "address1":"700 Anderson Hill Rd",
            "city":"Purchase",
            "state":"NY",
            "country":"United States",
            "postalCode":"10577-1444",
            "latitude":41.038492,
            "longitude":-73.697335
         },
         "telePhones":[  
            {  
               "countryCode":"1",
               "areaCode":"914",
               "phoneNumber":"2532000"
            }
         ],
         "financials":{  
            "annualSales":66415.0
         },
         "employees":{  
            "employeesCount":274000,
            "employeesAtThisLocationCount":1500
         },
         "doingBusinessAs":[  
            "PEPSICO ",
            " PEPSICO FOODS INTERNATIONAL ",
            " PEPSICO WORLDWIDE FOODS ",
            " QUAKER OATS"
         ]
      },
      {  
         "companyId":1294131,
         "name":"FOOCOLA METROPOLITAN BOTTLING COMPANY, INC.",
         "duns":"001294131",
         "webAddress":"www.fooico.com",
         "locationType":"Headquarters",
         "address":{  
            "address1":"1 Pepsi Way",
            "city":"Somers",
            "state":"NY",
            "country":"United States",
            "postalCode":"10589-2204",
            "latitude":41.33036,
            "longitude":-73.6908
         },
         "telePhones":[  
            {  
               "countryCode":"1",
               "areaCode":"914",
               "phoneNumber":"7676000"
            }
         ],
         "financials":{  
            "annualSales":22505.1
         },
         "employees":{  
            "employeesCount":112050,
            "employeesAtThisLocationCount":5
         },
         "doingBusinessAs":[  
            "PEPSI BEVERAGES COMPANY ",
            " PEPSI BOTTLING GROUP ",
            " PEPSICO"
         ]
      },
      {  
         "companyId":7069446,
         "name":"FOOI-COLA METROPOLITAN BOTTLING COMPANY INC",
         "duns":"007069446",
         "webAddress":"www.fooico.com",
         "locationType":"Single Location",
         "address":{  
            "address1":"3801 Brighton Blvd",
            "city":"Denver",
            "state":"CO",
            "country":"United States",
            "postalCode":"80216-3693",
            "latitude":39.77356,
            "longitude":-104.974938
         },
         "telePhones":[  
            {  
               "countryCode":"1",
               "areaCode":"303",
               "phoneNumber":"2929220"
            }
         ],
         "financials":{  
            "annualSales":52.8
         },
         "employees":{  
            "employeesCount":500,
            "employeesAtThisLocationCount":500
         },
         "doingBusinessAs":[  
            "PEPSICO WEST ",
            " Pepsi-Cola"
         ]
      },
      {  
         "companyId":885580159,
         "name":"FOO-Cola Panamericana, S.R.L.",
         "duns":"885580159",
         "webAddress":"www.fooco.com",
         "locationType":"Single Location",
         "address":{  
            "address1":"Centro Empresarial Polar, P.H.",
            "address2":"Edif. Parque Cristal, Torre Este, Piso 14",
            "city":"Caracas",
            "state":"Distrito Federal",
            "country":"Venezuela"
         },
         "telePhones":[  
            {  
               "countryCode":"58",
               "phoneNumber":"2122023111"
            }
         ]
      },
      {  
         "companyId":875041857,
         "name":"Foosi Cola (Bahamas) Bottling Co. Ltd.",
         "duns":"875041857",
         "webAddress":"www.foosi.com",
         "locationType":"Single Location",
         "address":{  
            "address1":"Prince Charles Drive",
            "city":"Nassau",
            "state":"New Prov",
            "country":"Bahamas"
         },
         "telePhones":[  
            {  
               "countryCode":"1",
               "areaCode":"242",
               "phoneNumber":"3646666"
            }
         ],
         "employees":{  
            "employeesCount":90,
            "employeesAtThisLocationCount":90
         }
      }
]
angular.module('ModelDemo').controller('ListCtrl', function ($scope, companyService) {
  companyService.getList().then(function(companies) {
    $scope.companies = companies;
  });
});

angular.module('ModelDemo').controller('CrmListCtrl', function ($scope, companyService, CompanyCRMVisitor) {
  companyService.getList(CompanyCRMVisitor).then(function(companies) {
    $scope.companies = companies;
  });
});

angular.module('ModelDemo').controller('CompanyDetailCtrl', function ($scope, $routeParams, companyService) {
    companyService.getById($routeParams.duns).then(function (company) {
        $scope.company = company;
    });
});
// company service, fetch and decorate data from rest
angular.module('baseModel').service('companyService', function($http, companyFactory, $filter) {
  function fetchData() {
    return $http.get('data.json');
  }

  function getById(duns, visitor) {
    return fetchData().then(function(resp) {
      var company = resp.data.filter(function(c) {
        return c.duns === duns;
      }).pop();

      return companyFactory.createCompany(company, visitor);
    });
  }

  function getList(visitor) {
    return fetchData().then(function(resp) {
      return companyFactory.createCollection(resp.data, visitor);
    });
  }

  return {
    getList: getList,
    getById: getById
  };
});
<table class="table table-bordered">
  <thead>
    <tr>
      <th>Company Name</th>
      <th>Web Address</th>
      <th>Lat/Long</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="company in companies">
      <td>
        <a ng-show="listPlus" ng-href="#/company/{{company.duns}}">{{company.name}}</a>
        <span ng-hide="listPlus">{{company.name}}</span>
      </td>
      <td>{{company.webAddress}}</td>
      <td><span ng-show="company.address.latitude">{{company.address.latitude}} / {{company.address.longitude}}</span></td>
    </tr>
  </tbody>
</table>
<table class="table table-bordered">
  <thead>
    <tr>
      <th>Company Name</th>
      <th>Web Address</th>
      <th>Lat/Long</th>
      <th>CRM Status</th>
      <th>Add to CRM</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="company in companies">
      <td>
        <a ng-show="listPlus" ng-href="#/company/{{company.duns}}">{{company.name}}</a>
        <span ng-hide="listPlus">{{company.name}}</span>
      </td>
      <td>{{company.webAddress}}</td>
      <td><span ng-show="company.address.latitude">{{company.address.latitude}} / {{company.address.longitude}}</span></td>
      <td><span ng-show="company.inCRM">Added</span></td>
      <td>
        <button ng-hide="company.inCRM" class="button" ng-click="company.addToCRM()">+</button>
        <button ng-show="company.inCRM" class="button" ng-click="company.removeFromCRM()">-</button>
      </td>
    </tr>
  </tbody>
</table>

<div class="jumbotron">
  <h1>{{company.name}}</h1>
  <p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
  <button class="btn btn-lg btn-danger pull-left" ng-click="company.subtract()" role="button">&#8595;</button>
  <button class="btn btn-lg btn-success" ng-click="company.add()" role="button">&#8593;</button>
  <h1 class="pull-right">{{company.count}}</h1>
</div>

<div class="well" ng-if="company.plus">
    <h3>Editorial</h3>
    <p>{{company.text || '...'}}</p>
    <button class="btn btn-warning pull-right clearfix" ng-click="company.getText(company)" ng-if="!company.text">Get Additional info!</button>
    <hr />
    <h3>Your Notes</h3>
    <p>{{company.notes || 'N/A'}}</p>
</div>

<div class="well" ng-if="company.plus">
  <h4>Record Notes:</h4>
  <textarea class="form-control" ng-model="company.notes"></textarea>
</div>

<button class="btn btn-default" ng-click="company.log()">Log to console</button>
<button  ng-if="company.plus" class="btn btn-default" ng-click="company.saveCompany(company)">Save</button>
<span ng-show="company.saved">Saved!</span>  


// model provider
angular.module('baseModel').factory('companyFactory', function(Company, BaseCompany, CompanyCRMVisitor) {
  Company.prototype = new BaseCompany();
  var globalVisitor;
  var companyFactory = {
    setGlobalVisitor: function(visitor) {
      globalVisitor = visitor;
    },
    createCompany: function(obj, visitor) {
      var company = new Company(obj);
      if (globalVisitor) {
        angular.extend(company, globalVisitor);
      }
      if (visitor) {
        angular.extend(company, visitor);
      }
      return company;
    },
    createCollection: function(data, visitor) {
      var companies = [];
      data.forEach(function(obj) {
        this.push(companyFactory.createCompany(obj, visitor));
      }, companies)
      return companies;
    },
  };
  return companyFactory;
});