FatalCatharsis
FatalCatharsis

Reputation: 3567

Dependency not injected although service exists

So I've been working on an Angular.js project for awhile now that will be used in a competition, and halfway through I realized I needed a plugin system to dynamically load some necessary code at runtime. Thus far i've found that angular.js isn't very accommodating to loading services at runtime. Due to the code only being available to competitors at competition time I can only provide an example of what isn't working.

// ex1.coffee

mainModule = angular.module('mainModule')

mainModule.service('needed', () ->
    @neededFunc() ->
        console.log "just an example"
    return this    

mainModule.service('ex1', ($injector) ->
    @loadFile = (u, pluginName) ->
        $.ajax
            dataType: "script",
            url: u,
            data: null,
            success: (jqxhr, textStatus) =>
                $injector.invoke([pluginName, (plugin) =>
                    plugin.testFunc()
            error: (jqxhr, textStatus, errorThrown) ->
                console.log "couldn't find the script"
    return this


// ex2.coffee

mainModule = angular.module('mainModule')

mainModule.service('ex2', (needed) ->
    @testFunc = () ->
        needed.neededFunc()
        console.log "in this dynamically loaded file"
    return this  

Implicit things: There is an existing module called mainModule. Jquery and angular have been previously included in the dom. ex1.js is already included in the dom while ex2.js has not yet been included in the dom. u is a url to the file ex2.js and pluginName = "ex2" . When the success handler is called, the invoke function fails, saying that an ex2Provider does not exist. However, if I breakpoint in the success callback and check the mainModule._invokeQueue, there does exist an array with the service name "ex2" so it definitely has an existing recipe within the module, but the $injector.invoke call still fails. If I modify the success handler to call, $injector.has pluginName, it fails. Can someone tell me how the module can have knowledge of a provider for this dynamically loaded service but not be able to inject it into a function call with $injector? The project is a yeoman project built with grunt.

Still learning angular so if there is some concept I'm missing or stating wrong, please tell me.

Upvotes: 1

Views: 96

Answers (1)

Reactgular
Reactgular

Reputation: 54801

AngularJS will bootstrap a module when it's loaded. During bootstrapping all the dependencies are initialized by the injector.

angular.module('mainModule').service('ex2', .....);

When you do the above via AJAX it happens after the mainModule has already been bootstrapped. The service() will queue the provider for bootstrapping, but bootstrapping never happens again.

This is from the manual on bootstrapping angular.

You should call angular.bootstrap() after you've loaded or defined your modules. You cannot add controllers, services, directives, etc after an application bootstraps.

What I think you can do is create a new module, and attach it to the existing module.

So your AJAX code might look like this.

angular.module('ex2Module',[]).service('ex2', .....);
angular.module('mainModule').requires.push('ex2Module');
$rootScope.$digest();

I can not test this, but I think this will cause the new module ex2Module to be bootstrapped and added as a dependency to the existing mainModule. The injector should see this module as queued for bootstrapping and install it.

Hope this helps.

Upvotes: 1

Related Questions