AngularJS dependency injection swap implementation

1.9k views Asked by At

I'm still learning AngularJS, and have a question regarding their flavor of dependency injection. For example purposes, say I have a DataProcessor service which has a processData method that takes in a uri parameter and it needs to read that data (which may be xml, json, etc.) and then perform some actions on it. The DataProcessor constructor takes in an implementation of a DataReader interface that knows how to read a certain file type. Here are some example services of what I'm talking about:

// implementations of the DataReader interface
myApp.service('XmlDataReader', function() {
    this.readData = function(uri) {
        // read xml data from uri
    }
}]);

myApp.service('JsonDataReader', function() {
    this.readData = function(uri) {
        // read json data from uri
    }
}]);

// data processing service that takes in an implementation of a DataReader
myApp.service('DataProcessor', ['DataReader', function(DataReader) {

    this.processData = function(uri) {
        var readData = DataReader.readData(uri);

        // process data and return it
    }
}]);

From a typical dependency injection perspective, a specific type of DataReader could be passed into the DataProcessor and used like so:

var dataProcessor = new DataProcessor(new JsonDataReader());
var processedData = dataProcessor.processData('dataz.json');

What is the AngularJS way of doing this?

3

There are 3 answers

2
drew_w On

Because of the way DI works - you shouldn't have to create instances of your services, ever really. What you do is you inject the service(s) you need into your controller and it should just work. In the case above, your controller might be defined to be:

var app = angular.module('App', ['DataProcessor']);

function MyController($scope, DataProcessor) {
    var uri = '';
    DataProcessor.processData(uri);
}

The only other thing you need to do here is make sure that "App" is the name you specify in the "ng-app" directive and make sure that your page includes the JS files with "DataProcessor" before you include the angular app module (technically these could even be defined in the same file). Hope this helps!

Edit

By the way, if you need to minify - the following is how you would define the controller:

var app = angular.module('App', ['DataProcessor']);

// if you need to minify:
var MyController = ['$scope', 'DataProcessor',
    function($scope, DataProcessor) {
        var uri = '';
        DataProcessor.processData(uri);
    }
];

Additional Suggestions

My understanding of a service at the present time is that is is used to shared data or code between controllers. If this data processing is specific to that controller you might consider just moving the "ProcessData" implementation directly into your controller. Sometimes changes like this can be simpler than processing the data in a service. If you do process the data in the service you might still want to write that data back to the scope. In this case, you can pass $scope as a parameter into the service routine. Since I don't know too much about your use case, just take these suggestions with a grain of salt. Good luck!

0
sss On

Do something like this:

myApp.service('DataProcessor', ['$injector', 'valueRecipeOfTheServicename', function($injector, valueRecipeOfTheServicename) {

    this.processData = function(uri) {
        var service = $injector.get(valueRecipeOfTheServicename);

        // process data and return it
    }
}]);

$injetcor.get() retrieves a service

0
Alvis On

Based on Noypi Gilas answer, I am initiating the controller with the name of the service and retrieving it via $injetcor.get():

myApp.service('DataProcessor', ['$injector', function($injector) {
    var service;

    $scope.init = function (serviceName) {
        service = $injector.get(serviceName);
    }

    this.processData = function(uri) {
        // use the service ...
    }
}]);