47

In the referenceI read:

Lastly, it is important to realize that all Angular services are application singletons. This means that there is only one instance of a given service per injector.

but with this simple code seems not to be a singleton

'use strict';
            angular.module('animal', [])
                .factory('Animal',function(){
                    return function(vocalization){
                        return {
                            vocalization:vocalization,
                            vocalize : function () {
                                console.log('vocalize: ' + this.vocalization);
                            }
                        }
                    }
                });    
                angular.module('app', ['animal'])
                    .factory('Dog', function (Animal) {
                        return Animal('bark bark!');
                    })
                    .factory('Cat', function (Animal) {
                        return Animal('meeeooooow');
                    })
                .controller('MainCtrl',function($scope,Cat,Dog){
                     $scope.cat = Cat;
                     $scope.dog = Dog;
                     console.log($scope.cat);
                     console.log($scope.dog);
                    //$scope.cat = Cat;
                });

I'm a little confused can you explain me what's the matter ?

UPDATE 1 May be I'm not the sharpest tool in the shed but afer the @Khanh TO reply it would be a better explanation in the reference it's not very clear.

UPDATE 2

'use strict';
            angular.module('animal', [])
                .factory('Animal',function(){
                        return {
                            vocalization:'',
                            vocalize : function () {
                                console.log('vocalize: ' + this.vocalization);
                            }
                        }

                });
                angular.module('dog', ['animal'])
                    .factory('Dog', function (Animal) {
                        Animal.vocalization = 'bark bark!';
                        Animal.color = 'red';
                        return Animal;
                    });

                angular.module('cat', ['animal'])
                   .factory('Cat', function (Animal) {
                        Animal.vocalization = 'meowwww';
                        Animal.color = 'white';
                        return Animal;
                    });
                 angular.module('app', ['dog','cat'])
                .controller('MainCtrl',function($scope,Cat,Dog){
                     $scope.cat = Cat;
                     $scope.dog = Dog;
                     console.log($scope.cat);
                     console.log($scope.dog);
                    //$scope.cat = Cat;
                });

BOOM it's a singleton !

UPDATE 3

But if you do like

'use strict';
            angular.module('animal', [])
                .factory('Animal',function(){
                    return function(vocalization){
                        return {
                            vocalization:vocalization,
                            vocalize : function () {
                                console.log('vocalize: ' + this.vocalization);
                            }
                        }
                    }
                });    
                angular.module('app', ['animal'])
                    .factory('Dog', function (Animal) {
                        function ngDog(){
                            this.prop = 'my prop 1';
                            this.myMethod = function(){
                                console.log('test 1');
                            }
                        }
                        return angular.extend(Animal('bark bark!'), new ngDog());
                    })
                    .factory('Cat', function (Animal) {
                        function ngCat(){
                            this.prop = 'my prop 2';
                            this.myMethod = function(){
                                console.log('test 2');
                            }
                        }
                        return angular.extend(Animal('meooow'), new ngCat());
                    })
                .controller('MainCtrl',function($scope,Cat,Dog){
                     $scope.cat = Cat;
                     $scope.dog = Dog;
                     console.log($scope.cat);
                     console.log($scope.dog);
                    //$scope.cat = Cat;
                });

it works

David Salamon
  • 2,361
  • 25
  • 30
Whisher
  • 31,320
  • 32
  • 120
  • 201

4 Answers4

54

It's singleton, there is only one object, but is injected into many places. (objects are passed by reference to a method)

All your Animal are object pointers referring to the same animal object which is a function in your case. Your Cat and Dog are objects constructed by this function.

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • because of I've got a php background it's sound a little odd for me. If in a php application I use a db singleton connection I can have only one connection and that's why singleton could be evil – Whisher Feb 01 '14 at 10:08
  • 2
    @Whisher: I updated my answer with information about `objects are passed by reference to a method`. Hope it's clearer for you. – Khanh TO Feb 01 '14 at 10:13
  • @Whisher: what is not clear for you? In your update 3, you create a `new object` using Animal function and extend that object with a new `ngDog`, `ngCat`. There are no modifications to `Animal` which is a function. – Khanh TO Feb 02 '14 at 02:21
  • In the update 2 the service is singleton the set up of Dog rewrite Cat. Cat has the same prop/method of Dog. – Whisher Feb 02 '14 at 09:44
  • @Whisher: in update 2, your `Animal`, `Dog`, `Cat` refer to the same object. – Khanh TO Feb 02 '14 at 09:51
  • Ok I got it :). Sorry to bother you again and again but could you give me an example without using angular.extend ? Be patience :) – Whisher Feb 02 '14 at 11:02
  • @Whisher: in your update 3, actually you don't need `angular.extend`, you just need to ensure you return a `new object`, not a reference to an existing one. – Khanh TO Feb 02 '14 at 12:10
  • Ouf, sorry I don't understand :( the only think I can think of is passing Animal as DI – Whisher Feb 02 '14 at 12:45
  • @Whisher: yes, you pass Animal as DI. The `Animal` you get as a DI is a `reference to the singleton Animal object`, if you modify anything using this reference, all other places injected with this `Animal` will notice the change. – Khanh TO Feb 02 '14 at 12:48
4

Yes, service is singleton. The following code log only one "M" to console:

function M() { console.log('M'); }
function M1(m) { console.log(1); }
function M2(m) { console.log(2); }
angular.module('app', [])
.service('M', M)
.service('M1', ['M', M1])
.service('M2', ['M', M2])
.controller('MainCtrl',function(M1, M2){});

run it in jsbin

3

Let me give an example about singletons in AngularJS. Lets assume that we have 2 controllers used in different part of our single page application:

myApp.controller('mainController', ['$scope', '$log', function($scope, $log) {

    $log.main = 'First Var';
    $log.log($log);

}]);

So now if we went to the page that is controlled via mainController controller, we will see this at the log:

enter image description here

As you see, the log object now contain my first variable.

Now here is my second controller:

myApp.controller('secondController', ['$scope', '$log', '$routeParams', function($scope, $log, $routeParams) {

    $log.secondVar = 'Second Var';
    $log.log($log);

}]);

So if you click on the second page that is controlled, you will see this:

enter image description here

Now get back to the first page:

enter image description here

What do you conclude about those 3 steps ?

Their is one $log object injected to the app. And as Tony Alicea said: This is a big memory save.

So this service, is called one time, and each time we are adding new variables and parameters to the same object and not adding different objects for different parameters.

Lara Ch
  • 165
  • 3
  • 12
2

Your example uses a factory, not a service. Note that provider is part of the game as well.

The best learning resource so far:

AngularJS: Service vs provider vs factory

There is an enlightening explanation by Miško Hevery and a practical example of factory, service and provider. Highly recommend.

Community
  • 1
  • 1
roland
  • 7,695
  • 6
  • 46
  • 61
  • I've used factory for a user case but angularjs reference refer to service as a whole (factory,service,provider) – Whisher Feb 01 '14 at 10:05
  • The factory/service distinction has nothing to do with it. The "factory" and "service" patterns are both just different ways of constructing a service. _All_ services are "singletons", regardless of how they're constructed. The issue with @Whisher's original example is that the function that constructs the Animal service doesn't return an object - it returns another function. So the Animal "service" is actually a function - still a "singleton", just not an object. The Animal "singleton" function is then used to construct the Dog and Cat services, which in this case are objects. (whew!) – Dan King Jun 28 '17 at 15:25