0

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>
8
  • I don't think Angular will pick up the newly added script once it's running Commented Sep 23, 2014 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 Commented Sep 23, 2014 at 6:49
  • scriptUrl???? WTF? that's not an accepted property of the route object! docs.angularjs.org/api/ngRoute/provider/$routeProvider Commented Sep 23, 2014 at 6:53
  • I assume you've set the ng-app attribute in HTML? Commented Sep 23, 2014 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. Commented Sep 23, 2014 at 6:55

2 Answers 2

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">');
                }
            }
Sign up to request clarification or add additional context in comments.

6 Comments

With resolve I also need to use deffered? because it will load template first and then the script file, what do you think?
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
I tried your above code but its loading the JS file after template rendering because of which I am getting same error
Tried the other opportunities? - Location-Change event should be the simplest one to grab
Thanks, finally resolve was my solution with deferred and promise.
|
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.