Giter Site home page Giter Site logo

wdi_9_angular_demo_factories's Introduction

Angular Factories and Services.

We are going to dive into Angular Factories and Services.

Objectives

Demo

Setup

We have copied all of the code from the last lesson wdi_9_angular_demo_routes into this repo to start off.

Oh, all but the app/customersData.js. And we'll see why we don't need this later.

Singleton

The Singleton Design pattern will prevent more than one instance of a class to occur. One can only create one instance of a Singleton.

Factories and Services are Singletons.

Singleton Pattern in Javascript

Built-in Services

These services are provided by Angular.

  • $http - Provides Ajax requests. Like the jQuery $.ajax we've used.
  • $location - Represent the Browser URL. Can access and change the Browser's URL.
  • $timeout
  • $window
  • $q - Provides a Promise that can be used to handle asynchronous callbacks.

Factories

Will return a custom object that can be used by multiple other components, typically controllers.

Factories use the Revealing Module Javascript Pattern to create the custom object they return.

Create a app/services/customerFactory.js
(function customersFactoryIIFE(){

  // Create a customers factory
  var customersFactory = function(){
    // customers is private, only available in this scope

    var factory = {};
    factory.customers = [];
    factory.customer = {};

    var customers = [
      {
        id: 1,
        joined: '2000-12-02',
        name:'John',
        city:'Chandler',
        orderTotal: 9.9956,
        orders: [
          {
            id: 1,
            product: 'Shoes',
            total: 9.9956
          }
        ]
      },
      {
        id: 2,
        joined: '1965-01-25',
        name:'Zed',
        city:'Las Vegas',
        orderTotal: 19.99,
        orders: [
          {
            id: 2,
            product: 'Baseball',
            total: 9.995
          },
          {
            id: 3,
            product: 'Bat',
            total: 9.995
          }
        ]
      },
      {
        id: 3,
        joined: '1944-06-15',
        name:'Tina',
        city:'New York',
        orderTotal:44.99,
        orders: [
          {
            id: 4,
            product: 'Headphones',
            total: 44.99
          }
        ]
      },
      {
        id: 4,
        joined: '1995-03-28',
        name:'Dave',
        city:'Seattle',
        orderTotal:101.50,
        orders: [
          {
            id: 5,
            product: 'Kindle',
            total: 101.50
          }
        ]
      }
    ]; // end of customers data

    factory.getCustomers = function(){
      return angular.copy(customers, factory.customers);
    };

    factory.getCustomer = function(customerId){
      for(var i=0, len=customers.length; i < len; i++){
        if(customers[i].id == parseInt(customerId)){
          return angular.copy(customers[i], factory.customer);
        }
      }
      return {};
    };
    return factory;
  };

  angular.module('customersApp').factory('customersFactory', customersFactory);
})();

  • Create an IIFE that will hide all the variables from Global scope.

  • Create a Self Revealing Function, customersFactory, in the IIFE.

  • In the customersFactory function we will:

    • hard code all the customer data.
      • We'll remove this when we get customer data from the back end
    • Create an empty object literal, "factory".
    • Create a method on factory, getCustomers, that can be used to access the customer data.
    • Create a method on factory, getCustomers, that given a customer id will return data for that customer.
    • return the object literal "factory".
  • Register the Angular Factory. So it's available throughout the application.

Add the customer factory to the index.html.
<!DOCTYPE html>
<html ng-app="customersApp">
  <head>
    <script src='bower_components/angularangular.js'></script>
    <script src='bower_components/angular-route/angular-route.js'></script>
    <script src='app/app_done.js'></script>
   	<!-- customer factory -->
    <script src='app/services/customersFactory_done.js'></script>
    <script src='app/controllers/customersController_done.js'></script>
    <script src='app/controllers/ordersController_done.js'></script>
  </head>

  <body>
    <div ng-view></div>
  </body>
</html>

Add the app/controllers/customersController.js
(function customersControllerIIFE(){

  // 1. Inject the customersFactory into this controller
  var CustomersController = function(customersFactory){
    this.sortBy = "name";
    this.reverse = false;
    // 2. Set the controllers array of customers equal to the factories
    this.customers= customerFactory.customers;

    // 3. Create a function that will set the customers Array in the scope
    // from the customersFactory
    function init(){
      // Init the customers from the factory
      customersFactory.getCustomers();
    }

    // 4. Initialize the controller.
    init();

    this.doSort = function(propName){
      this.sortBy = propName;
      this.reverse = !this.reverse;
    };

  };

 // Prevent the minifier from breaking dependency injection.
 CustomersController.$inject = ['customersFactory'];

 // The Controller is part of the module.
 angular.module('customersApp').controller('customersController', CustomersController);

})();
  1. Inject the customersFactory into this controller
  2. Set the controller's customers equal to the factory's customers within the scope.
  3. Create a function that will retrieve customers from our "DB".
  4. Initialize the controller.
Add the app/controllers/ordersController.js
(function ordersControllerIIFE(){

  var OrdersController = function($routeParams, customersFactory){
    var customerId = $routeParams.customerId;
    this.customer= customersFactory.customer;

    // private function, not available outside of IIFE

    function init(){
      // Search for the customer by id
      customersFactory.getCustomer(customerId);
    }

    init();
  };

  // Prevent the minifier from breaking dependency injection.
  OrdersController.$inject = ['$routeParams', 'customersFactory'];

  // The Controller is part of the module.
  angular.module('customersApp').controller('ordersController', OrdersController);

})();
  1. Inject the customersFactory into this controller
  2. Set the customer equal to the customerFactory's customer.
  3. Create a function, init, that will set the customers from the customerId param.
  4. Initialize the controller.
Add the app/views/orders.html
 <h3>{{ orderCtrl.customer.name}}'s Orders</h3>
 <table>
   <tr>
	<th>Product</th>
    <th>Total</th>
  </tr>
  <tr ng-repeat="order in customer.orders">
    <td>{{ orderCtrl.order.product }}</td>
    <td>{{ orderCtrl.order.total | currency }}</td>
  </tr>
</table>
<br/>

Defining Application Wide Variables.

Provide variables that don't belong in a factory, or controller. They are application wide variables that have values.

Add app settings to the app/services/values.js
// Create applicaton wide settings
angular.module("customersApp").value('appSettings', {
  title: "Customers Application",
  version: "1.0"
});

/*
// Use constant if you need app wide values available inside the app config
angular.module("customersApp").constant('appSettings', {
  title: "Customers Application",
  version: "1.0"
});
*/

Add the app settings to the scope for the customers View, app/controllers/customersController.js .
(function customersControllerIIFE(){

  // 1. Inject application wide value, appSetting.
  var CustomersController = function(customersFactory, appSettings){
	...
    // 2. Make the application wide settings available in the view.
    this.appSettings = appSettings;

	...

  };

 // 3. Prevent the minifier from breaking dependency injection.
 CustomersController.$inject = ['customersFactory', 'appSettings'];
 ...
})();

Use the app settings in the customers View, app/views/customers.html.
 <h3>{{ customersCtrl.appSettings.title}} </h3>
...
<span>Total customers: {{customersCtrl.customers.length}}</span>
<br/>
<br/>
<footer>Version: {{ customersCtrl.appSettings.version }}</footer>

Lab

Let's revist our songs app. Create a factory, pull all of the logic out of your controller and put it into your factory. We want to keep our controllers nice and neat.

Ajax (finally)

We are, finally, going to make remote API HTTP Request for the customer data. We will be using a very simple Rails app, actually created with the Rails API gem.

The repo for this API is Customers API.

But, we will need to setup the Angular Factories we created above to make Ajax calls to this API.

Modify the app/services/customersFactory.js
(function customersFactoryIIFE(){

  // Create a customers factory
  var customersFactory = function($http){
    var factory = {};
    factory.customers = [];
    factory.cutomer = {};

    factory.getCustomers = function(){
      // allow access to the list of customers
      return $http.get('http://localhost:3000/customers').success(function(response){
        angulr.copy(response, factory.customers);
      });
    };

    factory.getCustomer = function(customerId){
      return  $http.get('http://localhost:3000/customers/' + customerId).sucess(function(response)
        angular.copy(response, factory.customer);
      });
    };
    return factory;
  };

  customersFactory.$inject = ['$http'];

  angular.module('customersApp').factory('customersFactory', customersFactory);
})();

  • We've injected the Angular Ajax Service, $http , in to this Factory.
    • The $http behaves very much like the jQuery $.ajax
  • We have FINALLY removed the hard coded customers data from our app. And now are making a remote API request for this data. $http.get('http://localhost:3000/customers')
  • And we are making a HTTP GET Request for a specific customers data. $http.get('http://localhost:3000/customers/' + customerId)
  • Once this data comes back, we are setting the value of our factory to the response via angular copy. Angular copy is a deep copy. Not entirely sure what that means; don't care. It paves over any issues of timing or scope we might have.
  • Doing the weasel work of preventing the javascript minification problems. customersFactory.$inject = ['$http'];

Check out the $http Angular service. In each of the methods above we return a Promise from the $http service. A Promise will be invoked when the Ajax asynchronous request is returned from the server.

Modify the app/controllers/customersController.js
(function customersControllerIIFE(){

  var CustomersController = function(customersFactory, appSettings){

	...
    function init(){
      // Init the customers from the factory
      //this.customers = customersFactory.getCustomers();
      customersFactory.getCustomers()
    }

    init();
    ...

  };

 ...

})();

  • We have changed the init function to handle the Promise returned by the customersFactory.getCustomers method.
    • The anonymous function passed to success will fire and update the ViewModel's, scope, with the customers data.
    • The anonymous function passed to error will fire if there is an error communicating with the API.

Modify the app/controllers/ordersController.js

...
    function init(){
      // Search for the customer by id
      // this.customer = customersFactory.getCustomer(customerId);
      customersFactory.getCustomer(customerId)
    }

...
  • We have changed the init function to handle the Promise returned by the customersFactory.getCustomer(customerID) method.
    • The anonymous function passed to success will fire and update the ViewModel's, scope, with the customer data, this.customer.
    • The anonymous function passed to error will fire if there is an error communicating with the API.

Lab

Time to get serious with our songs app. Create an API (either in Node.js or Rails) and seed some songs. Then, refactor your angular app to read and render those songs.

Documentation

AngularJS

API Documentation

This is like the $.ajax in JQuery. Ajax HTTP Service

wdi_9_angular_demo_factories's People

Contributors

tdyer avatar maxblaushild avatar

Watchers

Peter Masucci avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.