Search code examples
javascriptangularjsangular-ui-routerangularjs-scope

AngularJS module.controller vs $controllerProvider.register


I'm trying to add controller dynamically in my AngularJS application. On sub-domain, I have anotherController.js file.
Here's anotherController.js content:

function anotherControllerWrapper() {
    return ['$scope', '$state', function ($scope, $state) {

        $scope.doWork = function () {
        //...doing some work...
            alert('work done');
        };

        $scope.doWork();
    }];
};

Also I have wrote runtimeController provider to be able to use $controllerProvider in runtime:

app.provider('runtimeController', function () {
    var controllerProvider = null;

    this.setControllerProvider = function (cp) {
        controllerProvider = cp;
    };

    this.$get = function () {
        return {
            registerController: function (controllerName, controllerConstructor) {
                if (!controllerProvider.has(controllerName)) {
                    controllerProvider.register(controllerName, controllerConstructor);
                }
            }
        };
    };
});

Here's config section of application:

app.config(function($controllerProvider, runtimeControllerProvider) {
    runtimeControllerProvider.setControllerProvider($controllerProvider);
});   

I'm receiving controller's code over http (inside another controller), so it looks like this:

app.controller('testController', ['$scope', '$state', '$http', 'runtimeController',
   function ($scope, $state, $http, runtimeController) {

    $http.get('http://someUrl/anotherController.js')
        .then(
            function(sucess){
                var evaluated = new Function('return ' + success.data)();
                var ctrl = evaluated();
                // routing to ui state with specified 'anotherController' works
                // no 'anotherController' in app._invokeQueue 
                runtimeController.registerController('anotherController', ctrl);
                // routing to ui state with specified 'anotherController' constanly fails
                // 'anotherController' appears in app._invokeQueue
                //app.controller('anotherController', ctrl);  

                //--registering new UI route with 'anotherController' as controller here

                $state.go('anotherState');

            },
            function(error){ alert('something went wrong!'); },
        );

}]);

Ui states are also added dymanically, after I'm adding controller.
Can someone explain me please, what's happening and what's difference between $controllerProvider.register and module.controller?


Solution

  • Module methods (controller, directive, etc) result in adding a config block (_configBlocks) that is executed on application initialization. Once the application has passed config phase, it won't execute newly added config blocks, so app.controller(...) has no effect during run phase.

    As this example shows, runtimeController implementation can be simplified to

    app.config(($provide, $controllerProvider) => {
      $provide.value('$controllerProvider', $controllerProvider);
    });
    

    eval should be avoided for numerous reasons. Considering that the script is loaded from a domain that is allowed by CORS and doesn't require to be evaled, a suitable alternative is to load it as a script. This will require to patch AngularJS API to allow late component registrations, similarly to how ocLazyLoad does - or just use ocLazyLoad, because it already does that.