Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create injectable class (constructor)

I'm an AngularJS newbie, and I'm just starting to understand the concepts and differences of factory, service and controller. As I understand, a factory is used to return a "value object" that can be injected. Most examples I've seen do something like this:

angular.module('module.factories', function() {
    factory('FactoryObject', function() {
        return {
            someFunction: function(someParam) {};
            someOtherFunction: function(someOtherParam) {});
        };
    });
});

In my controller, I want to be able to use this object, but I would like to initialize/instantiate it in the controller, since it can be re-initialized depending on events/actions in the controller. Therefore, I was wondering if I could return a constructor function in the factory instead?

angular.module('module.factories', function() {
    factory('FactoryObject', function() {

        function FactoryObject(initParam) {
        }

        FactoryObject.prototype.someFunction = function() {};

        return FactoryObject;

    });
});

Is this a suitable pattern for an angular factory? Or is it just "overkill" to use a factory for a custom object like this? Should I just include it in a library js file and reference it from there instead? One benefit of putting it in a factory is that it will be easy to mock it in a test since it will be injected where it's used. Are there any other mechanisms in Angular that could be used instead?

like image 662
NilsH Avatar asked Apr 17 '13 06:04

NilsH


People also ask

What does @inject do with constructor?

Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments. @Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.

What is constructor based injection?

This quick tutorial will explore a specific type of DI technique within Spring called Constructor-Based Dependency Injection, which simply put, means that we pass the required components into a class at the time of instantiation. Then we need to set up a Configuration file.

Can we use @autowired for constructor?

Using @Autowired After enabling annotation injection, we can use autowiring on properties, setters, and constructors.


4 Answers

I am having the same problem. Here's what I implemented:

angular.module('foo', [])
.factory('Foo', ['$http', function($http) {
  return function(a, b) {
    this.arr = [];
    this.a = a;
    this.b = b;
    this.random = Math.floor(Math.random()*11);

    this.someFunc = function(c) {
      this.arr.push(c);
    };
  };
}]);

Now, I can do:

var f1 = new Foo('A', 'B');
f1.random;  // i.e. 0
f1.someFunc('z');  // only affects f1.arr
var f2 = new Foo('C', 'D');
f2.random;  // i.e. 8
f2.someFunc('y');  // only affects f2.arr

Makes things more modularized. Hope that helps.

like image 55
Foo L Avatar answered Sep 21 '22 03:09

Foo L


You're quite right, this is the way to create injectable "classes" (constructors). However, using factory is only necessary when you need to inject other stuff to be used by your class:

.factory('Class', ['$q', function ($q) {
    function Class () {}
    Class.prototype = {
        constructor: Class,
        doSomeAsyncAction: function () {
            return $q(function (resolve, reject) {
                // ...
            });
        },
        // ...
    };
    return Class;
})

If you're creating a completely independent class (e.g. some data structure), you can just use constant, so that your class is available even in service providers:

(function (undefined) {
    function Class () {}
    Class.prototype = { ... };
    angular.module(...)
        .constant('Class', Class);
})();

As a sidenote, using provider is not going to help you in this. Providers serve a completely different purpose and (among other things) return a factory function that is then used _once_ on the first requested injection to create a single instance of the service. Providers are used to let you configure that single service instance in the _config phase_ of application construction (see documentation).

like image 33
hon2a Avatar answered Sep 21 '22 03:09

hon2a


I had the same question. I found a terrific article on how to setup Object-oriented AngularJS Services. Here's the article. Pay attention to the note about using Factories and not Services. It also covers extending your objects. I found it super easy to setup and it fit perfectly with Angular while keeping the Javascript simple and straight forward.

For Factory services which are singletons and do not need to be instantiated I use the object literal approach which has some performance advantages.

like image 43
Darryl Avatar answered Sep 23 '22 03:09

Darryl


Here's how I solved the issue:

(function (myModule) {
    'use strict';

    myModule.service('myService', [
        function() {
            var serivce = this;
            var obj = null;

            service.func1 = function(a, b) {

            }

            service.func2 = function(c, d) {
                . . .
                return something;
            }

            var constructor = function(myObj) {
                obj = myObj;

                return service;
            }

            return constructor;
        }
    ]);
}(angular.module("myModule")))

(function(myModule) {
    'use strict';

    myModule.controller('myController', [
        '$scope', 'myService', function($scope, myService) {

            var service = myService($scope.someObj);

            $scope.someOtherObj = service.func2($scope.a, $scope.b);

        }
    ]);
}(angular.module("myModule")))

The only thing returned from the service is the constructor and then the service returns the rest of the service.

like image 31
jolySoft Avatar answered Sep 21 '22 03:09

jolySoft