Thứ Hai, 11 tháng 7, 2016

AngularJS Service vs Factory

1. AngularJS custom services

We can define our own custom services in angular js app and use them wherever required.
There are several ways to declare angularjs service within application. Following are two simple ways:
var module = angular.module('myapp', []);

module.service('userService', function(){
 this.users = ['John', 'James', 'Jake'];
});
or we can use factory method
module.factory('userService', function(){
 
 var fac = {};
 
 fac.users = ['John', 'James', 'Jake']; 
 
 return fac;

});
Both of the ways of defining a service function/object are valid. We will shortly see the difference between factory() and service() method. For now just keep in mind that both these apis defines a singleton service object that can be used within any controller, filter, directive etc.

2. AngularJS Service vs Factory

AngularJS services as already seen earlier are singleton objects. These objects are application wide. Thus a service object once created can be used within any other services or controllers etc.
We saw there are two ways (actually four, but for sake of simplicity lets focus on 2 ways that are widely used) of defining an angularjs service. Using module.factory and module.service.
module.service( 'serviceName', function );

module.factory( 'factoryName', function );
When declaring serviceName as an injectable argument you will be provided with an instance of the function. In other words new FunctionYouPassedToService(). This object instance becomes the service object that AngularJS registers and injects later to other services / controllers if required.
When declaring factoryName as an injectable argument you will be provided with the value that is returned by invoking the function reference passed to module.factory.
In below example we define MyService in two different ways. Note how in .service we create service methods using this.methodname. In .factory we created a factory object and assigned the methods to it.

AngularJS .service

module.service('MyService', function() {
 this.method1 = function() {
   //..
  }

 this.method2 = function() {
   //..
  }
});

AngularJS .factory

module.factory('MyService', function() {
 
 var factory = {}; 

 factory.method1 = function() {
   //..
  }

 factory.method2 = function() {
   //..
  }

 return factory;
});

3. Injecting dependencies in services

Angularjs provides out of the box support for dependency management.
In general the wikipedia definition of dependency injection is:
Dependency injection is a software design pattern that allows the removal of hard-coded dependencies and makes it possible to change them, whether at run-time or compile-time. …
We already saw in previous tutorials how to use angularjs dependency management and inject dependencies in controllers. We injected $scope object in our controller class.
Dependency injection mainly reduces the tight coupling of code and create modular code that is more maintainable and testable. AngularJS services are the objects that can be injected in any other Angular construct (like controller, filter, directive etc). You can define a service which does certain tasks and inject it wherever you want. In that way you are sure your tested service code works without any glitch.
Like it is possible to inject service object into other angular constructs, you can also inject other objects into service object. One service might be dependence on another.
Let us consider an example where we use dependency injection between different services and controller. For this demo let us create a small calculator app that does two things: squares and cubes. We will create following entities in AngularJS:
  1. MathService – A simple custom angular service that has 4 methods: add, subtract, multiply and divide. We will only use multiply in our example.
  2. CalculatorService – A simple custom angular service that has 2 methods: square and cube. This service has dependency on MathService and it uses MathService.multiply method to do its work.
  3. CalculatorController – This is a simple controller that handler user interactions. For UI we have one textbox to take a number from user and two buttons; one to square another to multiply.
Below is the code:

3.1 The HTML

<div ng-app="app">
    <div ng-controller="CalculatorController">
        Enter a number:
        <input type="number" ng-model="number" />
        <button ng-click="doSquare()">X<sup>2</sup></button>
        <button ng-click="doCube()">X<sup>3</sup></button>
        
        <div>Answer: {{answer}}</div>
    </div>
</div>

3.2 The JavaScript

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

app.service('MathService', function() {
    this.add = function(a, b) { return a + b };
    
    this.subtract = function(a, b) { return a - b };
    
    this.multiply = function(a, b) { return a * b };
    
    this.divide = function(a, b) { return a / b };
});

app.service('CalculatorService', function(MathService){
    
    this.square = function(a) { return MathService.multiply(a,a); };
    this.cube = function(a) { return MathService.multiply(a, MathService.multiply(a,a)); };

});

app.controller('CalculatorController', function($scope, CalculatorService) {

    $scope.doSquare = function() {
        $scope.answer = CalculatorService.square($scope.number);
    }

    $scope.doCube = function() {
        $scope.answer = CalculatorService.cube($scope.number);
    }
});

3.3 Online Demo

Thus in the above angularjs injected service object to another service and in turn injected final service object to the controller object. You can inject same service object in multiple controllers. As angularjs service object is inheritedly singleton. Thus only one service object will be created per application.

4. End to End application using AngularJS Service

Let us apply the knowledge that we acquired so far and create a ContactManager application. This is the same app that we built-in our last tutorial. We will add a service to it and see how we can divide the code between service and controllers. Following are some basic requirements of this application:
  1. User can add new contact (name, email address and phone number)
  2. List of contacts should be shown
  3. User can delete any contact from contact list
  4. User can edit any contact from contact list
Following is the HTML code which defines a FORM to save new contact and edit contact. And also it defines a table where contacts can be viewed.

4.1 The HTML

<div ng-controller="ContactController">
    <form>
    <label>Name</label> 
        <input type="text" name="name" ng-model="newcontact.name"/>
    <label>Email</label> 
        <input type="text" name="email" ng-model="newcontact.email"/>
    <label>Phone</label> 
        <input type="text" name="phone" ng-model="newcontact.phone"/>
        <br/>
        <input type="hidden" ng-model="newcontact.id" />
     <input type="button" value="Save" ng-click="saveContact()" />
    </form>
<table>
<thead> 
<tr>
    <th>Name</th>
    <th>Email</th>
    <th>Phone</th>
    <th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in contacts">
    <td>{{ contact.name }}</td>
    <td>{{ contact.email }}</td>
    <td>{{ contact.phone }}</td>
    <td>
        <a  href="#" ng-click="edit(contact.id)">edit</a> | 
        <a href="#" ng-click="delete(contact.id)">delete</a>
    </td>
 </tr>
</tbody>
</table>    
</div>
Next we add the AngularJS code to and life to our ContactManager appllication. We define a module 'app'. This module is then used to create Service and Controller.
See in below code how ContactService is created. It has simple methods to save/delete/get the contact.
Note how the service object in injected in controller.

4.2 The JavaScript

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

module.service('ContactService', function () {
    //to create unique contact id
    var uid = 1;
    
    //contacts array to hold list of all contacts
    var contacts = [{
        id: 0,
        'name': 'Viral',
            'email': 'hello@gmail.com',
            'phone': '123-2343-44'
    }];
    
    //save method create a new contact if not already exists
    //else update the existing object
    this.save = function (contact) {
        if (contact.id == null) {
            //if this is new contact, add it in contacts array
            contact.id = uid++;
            contacts.push(contact);
        } else {
            //for existing contact, find this contact using id
            //and update it.
            for (i in contacts) {
                if (contacts[i].id == contact.id) {
                    contacts[i] = contact;
                }
            }
        }

    }

    //simply search contacts list for given id
    //and returns the contact object if found
    this.get = function (id) {
        for (i in contacts) {
            if (contacts[i].id == id) {
                return contacts[i];
            }
        }

    }
    
    //iterate through contacts list and delete 
    //contact if found
    this.delete = function (id) {
        for (i in contacts) {
            if (contacts[i].id == id) {
                contacts.splice(i, 1);
            }
        }
    }

    //simply returns the contacts list
    this.list = function () {
        return contacts;
    }
});

module.controller('ContactController', function ($scope, ContactService) {

    $scope.contacts = ContactService.list();

    $scope.saveContact = function () {
        ContactService.save($scope.newcontact);
        $scope.newcontact = {};
    }


    $scope.delete = function (id) {

        ContactService.delete(id);
        if ($scope.newcontact.id == id) $scope.newcontact = {};
    }


    $scope.edit = function (id) {
        $scope.newcontact = angular.copy(ContactService.get(id));
    }
})

Không có nhận xét nào:

Đăng nhận xét