1

So I have a tree helper object in my angular app that provides a bunch of useful functions for dealing with the many tree structures in the app. This is provided by a factory, so each time some controller (or whatever) asks for a tree helper, it gets its own copy:

angular.module('MainApp').factory('TreeHelperFactory', 
function ($http, $q, $filter, DataService) {
    var treeHelper = new function ($http, $q, $filter, DataService) {
    ...
    code
    ...
})

Now I have a category service that provides various category-related functions, including returning a tree of categories and providing ways to manipulate that tree. So thinking like an OO developer, I reckon that the category service is really like a subclass of my tree helper, so I inject a tree helper instance and then attempt to extend that instance with category-specific functions, naively like this:

angular.module('MainApp').provider('CategoryService',
function() {
    this.$get = function ($http, $q, $filter, DataService, TreeHelperFactory) {
        var catService = TreeHelperFactory; 
        catService.listCategories = function() {...}
        catService.deleteCategory = function(id) {...}
        ... more code...
        return catService;
        }  
    }
);

But this doesn't work. The new properties are not visible when I try to invoke them later, only the tree helper properties of the original factory object. What am I missing and how do I achieve this?

2 Answers 2

1

Services in angular.js are singletons, which means each time you inject a service it returns the exact same object, you cannot force it to return a new object each time it is injected.

What you can do is to create a Class function, for example:

angular.module('MainApp')

.factory('TreeHelperFactory', 
function ($http, $q, $filter, DataService) {

    /**
     * TreeHelper constructor
     * @class    
     */
    function TreeHelper() { /* this.init(); */ }

    /**
     * TreeHelper static class method
     */
    TreeHelper.list = function() { /* ... */ };

    /**
     * TreeHelper instance method
     */
    TreeHelper.prototype.init = function() { /*...*/ };

    /**
     * Returns the class
     */
    return TreeHelper;
})

Now you can inject it, instantiate an object and extend it like so:

.factory('CategoryService', function (TreeHelperFactory) {

  var catService = new TreeHelperFactory();

  catService.listCategories = function() { /*...*/ };
  catService.deleteCategory = function(id) { /*...*/ };

  return catService;
});

You can also use javascript prototypical inheritance but I think it's an overkill for most cases so keep it simple if you can.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks - I was aware that a service is a singleton, but reading the docs about factories I was less sure about the status of what they return. I'll try the above method and see if that does the job.
0

Helper that depends on Angular services is a bad smell for me - almost like its status is more than a helper and it has a service or two in it. Would you be able to rethink this through restructuring their roles and responsibilities?

1 Comment

"Helper" is the best word I could come up with, but what I really mean is an injectable object that provides certain functionality and which is not a singleton, because I need it to store data on it. Basically a class that can be extended.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.