1

So I didn't get an answer from my last question so I decided to handle this myself.

I created a generic controller like this:

.controller('GenericListController', function () {

    // Define this
    var self = this;

    // Define our list
    self.list = [];

    // Create our page sizes array
    self.pageSizes = [10, 20, 50, 100];

    // For filtering and sorting the table
    self.pageSize = self.pageSizes[0];
    self.predicate = 'name';
    self.reverse = false;
    self.filter = '';

    // For deleting
    self.delete = function (e, model) {

        // Delete the item
        service.delete(model.id);
    };
});

very simple as you can see. Now I was using this by injecting it into my controller like this:

.controller('DashboardController', ['GenericListController', 'CenterService', 'companyId', 'centers', function (Controller, service, companyId, centers) {

    // Assign this to a variable
    var self = Controller;
}])

In theory everything that is assigned to the GenericListController is now available to the DashboardController. The problem is the line in the generic controller that looks like this:

service.delete(model.id);

Somehow I need to reference my service in the generic controller. I thought that maybe I could create a provider and inject the service reference into the constructor but I am not sure if it being a singleton is an issue, so I need some help.

  1. Is a service / factory / provider a good way to build the GenericListController?
  2. Does a service / factory being a singleton affect anything? If so, can they be created so they are not singletons?
  3. Is there another way to achieve what I am after?

Update 1

So it appears some people are confused....

So if I created a factory that looks like this:

.factory('ListControllerService', function () {

    // Default constructor expecting a service
    return function (service) {

        // Define this
        var self = this;

        // Define our list
        self.list = [];

        // Create our page sizes array
        self.pageSizes = [10, 20, 50, 100];

        // For filtering and sorting the table
        self.pageSize = self.pageSizes[0];
        self.predicate = 'name';
        self.reverse = false;
        self.filter = '';

        // For deleting
        self.delete = function (e, model) {

            // Delete the item
            service.delete(model.id);
        };
    };
})

then I create 2 separate controllers that looks like this:

.controller('DashboardController', ['ControllerService', 'CenterService', 'companyId', 'centers', function (Controller, service, companyId, centers) {

    // Assign this to a variable
    var self = new Controller(service);

    self.list = centers;
}])

.controller('CompanyController', ['ControllerService', 'CompanyService', 'ArrayService', 'companies', function (Controller, service, arrayService, centers) {

    // Assign this to a variable
    var self = new Controller(service);

    self.list = companies;
}])

Hopefully you can see that the service I am injecting into the ListControllerService is different for each controller. The only caveat I have with my example is that each "service" must have a delete method (not so difficult because they are all api services).

I hope that explains things better.

Community
  • 1
  • 1
r3plica
  • 13,017
  • 23
  • 128
  • 290
  • What you want is a service/factory not a controller . Why would you not want singleton? As a singleton it is same instance across whole app – charlietfl Jul 14 '15 at 21:45
  • Actually, I was thinking after this that perhaps I want a provider, not a service or factory, because isn't a controller a provider? I am not sure if I want a singleton or not. I just need to be able to inject my "services" into the generic controller. – r3plica Jul 14 '15 at 21:50
  • depends on your needs...use provider if it needs to be configurable – charlietfl Jul 14 '15 at 21:52
  • The only thing I need to configure is what "dependencies" need to be injected. For example, more of these controller that depend on the GenericListController need to pass one service to the GenericListController and perhaps some other things that I have not thought of yet, but for now, it is only one service. If I can do that with a service or factory or provider then I will use the easiest. But I need someone to point me in the right direction. – r3plica Jul 14 '15 at 21:58
  • Service is used to share data and methods between components in your app ...controllers, directives and even other services. Not sure where the idea is coming from for "GenericController". It may even be that what you are wanting is a directive for lists as well as a service for methods. Higher level objective is not totally clear – charlietfl Jul 14 '15 at 22:09
  • Meh, have you ever built an MVC application and realised that the controllers are similar in that the list ones look the same, the save ones look the same, etc? That is what I want to eliminate to make everything DRY. – r3plica Jul 14 '15 at 22:18
  • Looking at prior question it still seems like you are taking an unorthodox approach to working with angular. The one good thing is it's modularity...if you see you are duplicating...consolidate using a service, directive or combination of both or whatever is applicable to the situation. – charlietfl Jul 14 '15 at 22:21
  • isn't creating a service to handle similar controllers doing exactly that? – r3plica Jul 14 '15 at 22:35

2 Answers2

0

The solution I am using on my current project is to write a function that registers a factory.

function CreateMyGenericFactory(factoryName, serviceName)
    app.factory(factoryName, [serviceName, function (service){

        var GenericListFactory = {
            list: [],
            pageSizes: [10, 20, 50, 100],
            predicate: 'name',
            reverse: false,
            filter: ''
            delete: function(e, model){
                service.delete(model.id);
            }
        }

        GenericListFactory.pageSize = GenericListFactory.pageSizes[0];

        return GenericListFactory;

    }]);
}

Then execute the function in your JS to register a new factory dynamically.

CreateMyGenericFactory('ListFactory', 'ListService');

And use it in your controller.

app.controller('GenericListController', ['ListFactory', function (ListFactory) {
  ...
  console.log(ListFactory.pageSizes.length); // -> 4
  ListFactory.delete(e, model);
}];

I also registered the service inside my CreateMyGenericFactory function to further reduce duplication, you may consider doing that as well if each factory has its own service.

The final solution looked like this.

function CreateMyGenericFactory(name)
    var factoryName = name + 'Factory'; // e.g. listFactory
    var serviceName = name + 'Service'; // e.g. listService

    app.factory(factoryName, [serviceName, function (service){

        var factory = {
            list: [],
            pageSizes: [10, 20, 50, 100],
            predicate: 'name',
            reverse: false,
            filter: ''
            delete: function(e, model){
                service.delete(model.id);
            }
        }

        factory.pageSize = factory.pageSizes[0];

        return factory;

    }]);

    app.service(serviceName, ['$resource', function (resource){

        return $resource(Modus[backend] + '/api/'+slug+'s/:id', {
            id: '@_id'
        });

    }]);
}

That way I could register all the Factories and Services I needed without duplicating any code.

CreateMyGenericFactory('user');
// this registered userFactory and userService
mz3
  • 1,314
  • 11
  • 27
  • how would I go about using that in my controller? do I inject it and then do self = new GenericListFactory(serviceName) ? – r3plica Jul 14 '15 at 22:13
  • In fact I don't think this will work because it uses implicit annotations which will not be minified. Am I right? – r3plica Jul 14 '15 at 22:16
  • You are correct - I'll change it to the minify safe version and give controller code. – mz3 Jul 14 '15 at 22:18
  • Cheers, will really help :) – r3plica Jul 14 '15 at 22:19
  • Ok, I can see where you are going with this. The solution you have presented is not Generic because the GenericListFactory always expects the same service to be passed. What I am trying to do is create a service where the only things that is shared is the variables, the MyOtherService parameter is a service that is yet to be defined. I will update my question in the hope that someone will understand. – r3plica Jul 14 '15 at 22:22
  • Oh you need to pull in `MyOtherService` dynamically? I'll update the question. – mz3 Jul 14 '15 at 22:23
  • I have update my question with a more thorough explaination – r3plica Jul 14 '15 at 22:27
0

Well, it looks like I was nearly there. I have managed to solve this but I am not sure if it is the best way to do it, but it certainly works.

So my service is very similar to before:

.factory('ListControllerService', function () {

    // Default constructor expecting a service
    return function (ctrl, service) {

        // Define this
        var self = ctrl;

        // Define our list
        self.list = [];

        // Create our page sizes array
        self.pageSizes = [10, 20, 50, 100];

        // For filtering and sorting the table
        self.pageSize = self.pageSizes[0];
        self.predicate = 'name';
        self.reverse = false;
        self.filter = '';

        // For deleting
        self.delete = function (e, model) {

            // Delete the item
            service.delete(model.id);
        };

        return self;
    };
})

The only thing that changes is that instead of:

self = this;

I now do:

self = ctrl;

ctrl is the controller that is inheriting from this service. I also return self so that I can bind it to self in the inherited controller.

now my controller looks like this:

.controller('DashboardController', ['ListControllerService', 'CenterService', 'companyId', 'centers', function (Controller, service, companyId, centers) {

    // Assign this to a variable
    var self = new Controller(this, service);

    console.log(this);
    console.log(self);

    // Assign our centers
    self.list = centers;
}])

both console.log output the same which is great.

r3plica
  • 13,017
  • 23
  • 128
  • 290