Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have an application in angularjs where each controller is in written in different JS files.

I need to call those files only on routechange event. For now I am successful in getting the appropriate controller file, but for some reason its throwing error:

Error: [ng:areq] Argument 'ApplicantsController' is not a function, got undefined
http://errors.angularjs.org/1.2.25/ng/areq?p0=ApplicantsController&p1=not%20a%20function%2C%20got%20undefined
minErr/<@http://localhost/talentojo/app/js/angularjs/angular.js:78:5
assertArg@http://localhost/talentojo/app/js/angularjs/angular.js:1509:5
assertArgFn@http://localhost/talentojo/app/js/angularjs/angular.js:1520:76
$ControllerProvider/this.$get</<@http://localhost/talentojo/app/js/angularjs/angular.js:7278:9
nodeLinkFn/<@http://localhost/talentojo/app/js/angularjs/angular.js:6670:13
forEach@http://localhost/talentojo/app/js/angularjs/angular.js:332:11
nodeLinkFn@http://localhost/talentojo/app/js/angularjs/angular.js:6657:11
compositeLinkFn@http://localhost/talentojo/app/js/angularjs/angular.js:6105:13
publicLinkFn@http://localhost/talentojo/app/js/angularjs/angular.js:6001:30
z/<.link@https://code.angularjs.org/1.2.25/angular-route.min.js:7:388
nodeLinkFn@http://localhost/talentojo/app/js/angularjs/angular.js:6712:1
compositeLinkFn@http://localhost/talentojo/app/js/angularjs/angular.js:6105:13
publicLinkFn@http://localhost/talentojo/app/js/angularjs/angular.js:6001:30
createBoundTranscludeFn/boundTranscludeFn@http://localhost/talentojo/app/js/angularjs/angular.js:6125:1
controllersBoundTransclude@http://localhost/talentojo/app/js/angularjs/angular.js:6732:11
v@https://code.angularjs.org/1.2.25/angular-route.min.js:6:355
$RootScopeProvider/this.$get</Scope.prototype.$broadcast@http://localhost/talentojo/app/js/angularjs/angular.js:12980:15
l/<@https://code.angularjs.org/1.2.25/angular-route.min.js:11:127
qFactory/defer/deferred.promise.then/wrappedCallback@http://localhost/talentojo/app/js/angularjs/angular.js:11572:15
qFactory/defer/deferred.promise.then/wrappedCallback@http://localhost/talentojo/app/js/angularjs/angular.js:11572:15
qFactory/ref/<.then/<@http://localhost/talentojo/app/js/angularjs/angular.js:11658:11
$RootScopeProvider/this.$get</Scope.prototype.$eval@http://localhost/talentojo/app/js/angularjs/angular.js:12701:9
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost/talentojo/app/js/angularjs/angular.js:12513:15
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost/talentojo/app/js/angularjs/angular.js:12805:13
done@http://localhost/talentojo/app/js/angularjs/angular.js:8378:34
completeRequest@http://localhost/talentojo/app/js/angularjs/angular.js:8592:7
createHttpBackend/</xhr.onreadystatechange@http://localhost/talentojo/app/js/angularjs/angular.js:8535:1

My code: HTML

<body>

    <!-- Header Starts -->
    <div ng-include="'assets/defaults/header.html'"></div>
    <!-- Header Ends -->
    <ul>
        <li>
            <a href="#">Home</a>
        </li>
        <li>
            <a href="#applicants">Applicants</a>
        </li>
    </ul>
    <div ng-view></div>

    <!-- Footer Starts -->
    <div ng-include="'assets/defaults/footer.html'"></div>
    <!-- Footer Ends -->
</body>

Route:

var app = angular.module('TOJO',['ngRoute']);

app.config(function($routeProvider, $locationProvider) {
    $routeProvider.when('/', {
        templateUrl : 'assets/home.html',
        controller  : 'HomeController'
    }).when('/applicants', {
        templateUrl : 'assets/applicants/list.html',
        scriptUrl   : 'assets/applicants/applicants.js'
    });
}).
run(function($rootScope, $location) {
    $rootScope.$on( "$routeChangeStart", function(event, next, current) {
        if(next.scriptUrl !== undefined)
            loadScript(next.scriptUrl);
    });
});

app.controller('HomeController', function($scope) {
    $scope.message = 'Look! I am home page.';
});

var loadScript = function(url, type, charset) {
    if (type===undefined) type = 'text/javascript';
    if (url) {
        var script = document.querySelector("script[src*='"+url+"']");
        if (!script) {
            var heads = document.getElementsByTagName("head");
            if (heads && heads.length) {
                var head = heads[0];
                if (head) {
                    script = document.createElement('script');
                    script.setAttribute('src', url);
                    script.setAttribute('type', type);
                    if (charset) script.setAttribute('charset', charset);
                    head.appendChild(script);
                }
            }
        }
        return script;
    }
};

And the file applicants.js which is getting called on route change:

app.controller('ApplicantsController',['$scope', function($scope) {
    $scope.message = 'Look! I am home page.';
}
]);

list.html:

<div ng-controller="ApplicantsController">{{message}}</div>
share|improve this question
    
I don't think Angular will pick up the newly added script once it's running –  Anzeo Sep 23 at 6:48
1  
Its loading the script, I checked in firebug but I thk for some reason its loading the template first and then the js file because of which its throwing that error –  ntechi Sep 23 at 6:49
    
scriptUrl???? WTF? that's not an accepted property of the route object! docs.angularjs.org/api/ngRoute/provider/$routeProvider –  Josep Sep 23 at 6:53
    
I assume you've set the ng-app attribute in HTML? –  Anzeo Sep 23 at 6:55
    
@Josep That is a key which is added by me, you can add your own key value so that you can use it further, and thats working. –  ntechi Sep 23 at 6:55

2 Answers 2

Had you already a look, into: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider resolve event?

"If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated." -> Maybe you can insert your script here into the body / head tag and return a promise.

I use this to include stylesheets e.g.

resolve: {
                style: function () {
                    angular.element('head').append('<link  href="*.css" rel="stylesheet">');
                }
            }
share|improve this answer
    
With resolve I also need to use deffered? because it will load template first and then the script file, what do you think? –  ntechi Sep 23 at 7:09
    
If you use deffered, the resolve will wait, till your promise is called, maybe also the template rendering - I don't know to be honest. Another way is to grab the location.startChange event: docs.angularjs.org/api/ng/service/$location A third way would be: $compile, you could $compile your own templates, and before you start compile, you could add your Script-Tag. I have a actual issue with this, but a simple demo you can find at least here: plnkr.co/edit/7W9AbpaLqsYCecOWdIlz?p=preview Have a look into the UIService and the RouteProvider.js –  graphefruit Sep 23 at 7:41
    
I tried your above code but its loading the JS file after template rendering because of which I am getting same error –  ntechi Sep 23 at 7:43
    
Tried the other opportunities? - Location-Change event should be the simplest one to grab –  graphefruit Sep 23 at 9:42
    
Thanks, finally resolve was my solution with deferred and promise. –  ntechi Sep 23 at 10:00
up vote 0 down vote accepted

So finally I found the solution:

I used resolve, to add script in my document dynamically. So my route:

var app = angular.module('TOJO',['ngRoute']).service('HttpTojoService', Service);
app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
    app.controllerProvider = $controllerProvider;
    app.compileProvider    = $compileProvider;
    app.routeProvider      = $routeProvider;
    app.filterProvider     = $filterProvider;
    app.provide            = $provide;

    $routeProvider.when('/', {
        templateUrl : 'assets/home.html',
        controller  : 'HomeController'
    }).when('/applicants', {
        templateUrl : 'assets/applicants/list.html',
        controller  : 'ApplicantsController',
        resolve:{deps:function($q, $rootScope){
            return routeResolver($q.defer(),['assets/applicants/applicants.js'],$rootScope);
        }}
    }).when('/jobs', {
        templateUrl : 'assets/jobs/list.html',
        controller  : 'JobsController',
        resolve:{deps:function($q, $rootScope){
            return routeResolver($q.defer(),['assets/jobs/jobs.js'],$rootScope);
        }}
    });
});

function routeResolver(deferred,dependencies,$rootScope){
    $script(dependencies, function()
    {
        $rootScope.$apply(function()
        {
            deferred.resolve();
        });
    });
    return deferred.promise;
}

and my controller:

app.controllerProvider.register('ApplicantsController',['$scope', 'HttpTojoService', function($scope, HttpTojoService) {
    $scope.message = 'Look! I am applicants page.';
}
]);

And also you'll also need scripts.js

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.