In one of the sample apps I am working on to try to master AngularJS I’ve encountered an unexpected challenge, I need to be able to reuse a service that will feed data coming from two different sources to the same controller running in two different environments.

Let’s try to make a little more sense of that. I have an app that is going to be running in two different environments, a chrome packaged app, and mobile.

Both apps are going to be sharing the same UI (views), controllers, filters, and directives. However the data feeding both apps’ controllers is coming from a different source.

In the chrome packaged app the data is going to be loaded from the chrome.storage.local API, and the mobile app will load the data from a REST API remote server.

The challenge here is making the controllers data source agnostic, they should not care where the data is coming from. This way we keep only one version of the code reused in both applications.

First, I am going to create two different factories, one for the chrome app, and the other one for mobile. They both have the same public signature, returning the same method names. Methods that are going to be used in the controllers:

    // The first factory
    angularApp.factory('chromeAppFactory', function() {
        // Private methods to access the data
        var chromeAppGet = function() {
           return { name: 'chrome app service', value: 'angular is indeed cool' };
        };
         // Public methods, they should have the same signature in both services
        return {
             get: chromeAppGet
        }
    }); 

And the second factory, for mobile:

    // The second factory
    angularApp.factory('mobileAppFactory', function() {
        // Private methods to access the data
        var mobileAppGet = function() {
             return { name: 'mobile app service', value: 'cool angular indeed is' };
        };
         // Public methods, they should have the same signature in both services
        return {
             get: mobileAppGet
        }
    });

So far so good, now how do we pick and choose which one to use? Let’s use a provider to configure the app data source:

    // The provider
    angularApp.provider('dataSource', function() {
        var environment = 'chrome';
        // I know this is ridiculous
        this.setEnvironment = function(value) {
            environment = value;
        };
        this.$get = ['chromeAppFactory', 'mobileAppFactory', function(chromeAppFactory, mobileAppFactory) {
            // environment !== 'chrome' ... yes, don't judge!
            return environment !== 'chrome' ? mobileAppFactory : chromeAppFactory;
        }];
    });

All right, finally I find a good use of providers. I have to admit up until now I’ve been using a specific configuration service/factory, but I like this much better.

The setEnvironment() method is going to be invoked inside app.config() during the config phase, and the dataSourceProvider will return the proper factory depending on which one has been selected.

Also not that the way I’ve written it here is pretty dull, I probably should create public setChrome() and setMobile() methods, and some private isChrome() and isMobile() checks for $get to use, instead of doing things like environment !== ‘chrome’. But for clarity I chose to leave it this simple.

Now, we need a third factory, or service if you prefer, that will be the one injected into the controllers. This data service is the one that will be pulling the data from the previous two factories:

    // The data service used in the controllers
    angularApp.factory('dataService', ['dataSource', function(dataSource) {
        // Add some business logic in here, massage the data if you want
        // or extend the factory object before returning it to the controllers.
        // Return the factory, the providers's $get() method is invoked here.
        return dataSource;
    }]);

You can actually skip this by using dataSource directly in your controllers, but I am using angular-ui-router and I like storing my ‘scopes’ in services instead of controllers directly. This way I can bind multiple controllers to the same variables and keep the same values in different nested states. By using this dataService in between the controllers and the factories, I have the perfect place to expose some methods or properties that otherwise will clutter the controllers very quickly.

All right, last step, configuring the provider. This is usually done in the main app.js file of your angular application, where you declare your routes:

    // The configuration stage
    angularApp.config(['dataSourceProvider', function(dataSourceProvider) {
        dataSourceProvider.setEnvironment('chrome');
        // or 'anything else'
    }]);

And finally, the single controller that we use in the UI:

    // The controller
    angularApp.controller('DataController', ['dataService', function(dataService) {
        this.data = dataService.get();
        // dataService.get() calls either chromeAppGet() or mobileapGet()
        // depending on which one we selected during config()
    }]);

Why?
Well, for starters if you use Grunt.js to build your dist, you only need to replace the ‘chrome’ string in the config method to whatever environment you are building. No need to create branches, change settings manually, or keeping multiple copies anymore unless you really need to.

In the provider you can configure more settings for your factories. For example in my chromeAppFactory I might need to store the names of the table keys that chrome.storage.local will use, and the provider might be a better place to set that up instead of directly encoding them as strings in your factory.

Issues?
Remember that once the config phase ends, the provider has been configured and you won’t be able to change it, it will be set to either use chromeAppFactory or mobileAppFactory. Is that a problem? Not for me, this is exactly what I need, but you might want to sit down and think about if it suits your needs.

Before I go, as usual here is a plunk with the whole thing working.