Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I'm trying to inject my controllers in AngularJS using the ui.router resolve, but i'm having issues where the template is loading first. My app.js is defined below:

var app = angular.module('app', ['ui.router']);

app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider.otherwise('/');

    $stateProvider
        .state('view1', {
            url: "/view1/",
            templateUrl: "view1/view1.html",
            resolve: {'': appendStaticFileToPage("view1/view1.js")}
        })

        .state('view2', {
            url: "/view2/",
            templateUrl: "view2/view2.html",
            resolve: {'': appendStaticFileToPage("view2/view2.js")}
        })

    ;

    function appendStaticFileToPage(file) {
        return function($q){
            var script = document.createElement('script');
            script.setAttribute('src', file);
            document.getElementsByTagName('head')[0].appendChild(script);
        };
    }

});

When the user clicks on a particular link for a state, I inject the js file for it into the browser. That part works fine. But the templateUrl defines the template to be used and that always loads first. Since it defines an ng-controller what happens is that the controller is being loaded, but since its not in the page yet, I get an error in the console that the controller couldn't be found. How can I achieve this?

I have created a simplified version of my code here:

http://plnkr.co/edit/X9uFOMIJq5tCn7VezSzT?p=preview

Is there a way to achieve what I am doing here?

EDIT

I previously had promises on my append code, which I'm showing below:

function appendStaticFileToPage(file) {
    return function($q){
        var deferred = $q.defer();

        var script = document.createElement('script');
        script.setAttribute('src', file);
        document.getElementsByTagName('head')[0].appendChild(script).ready(function(){
            deferred.resolve();
        });

        return deferred.promise;
    };
}

When I do this, nothing happens. It doesn't load the template. It also doesn't show the console.log statements I put in the controllers.

share|improve this question
1  
you forgot to return a promise that gets resolved when the controller has been loaded to your resolve function. – Kevin B Jul 31 '15 at 19:42
    
I tired to do that also. But then the template doesn't get loaded. Seems like nothing happens. I even put console statements in my controllers and they don't appear. – KVISH Jul 31 '15 at 19:44
    
my guess would be either the script failed to load, or the way you're listening for it to load is incorrect. – Kevin B Jul 31 '15 at 19:45
1  
Your edit is close, but, you're still not resolving the deferred, you just created it and then never used it. – Kevin B Jul 31 '15 at 19:48
1  
The script is properly loading, and it's not being resolved until after the script is loaded, so the problem lies with how (or when) we're attaching the controller to the module. Delaying it longer has no effect which rules out any race conditions. – Kevin B Jul 31 '15 at 20:11
up vote 1 down vote accepted

Since registering it with .controller isn't working, and I can confirm that the script is loading properly and executing, I tried instead injecting the $controllerProvider service and registering the controller through there, and it works.

app.config(function($stateProvider, $urlRouterProvider, $controllerProvider) {

    $urlRouterProvider.otherwise('/');

    $stateProvider
        .state('view1', {
            url: "/view1/",
            templateUrl: "view1.html",
            resolve: {'': appendStaticFileToPage("view1.js", "MyViewOneController")}
        })

        .state('view2', {
            url: "/view2/",
            templateUrl: "view2.html",
            resolve: {'': appendStaticFileToPage("view2.js", "MyViewTwoController")}
        })

    ;

    function appendStaticFileToPage(file, controllerName) {
        return function($q){
            var deferred = $q.defer();

            var script = document.createElement('script');
            script.onload = function () {
                $controllerProvider.register(controllerName, window[controllerName])
                deferred.resolve();
            };
            script.onerror = function () {
                deferred.reject();
            };
            script.setAttribute('src', file);
            document.getElementsByTagName('head')[0].appendChild(script);

            return deferred.promise;
        };
    }

});

I had to also update view1.js and view2.js as follows:

'use strict';
window.MyViewOneController = function($scope) {
  console.log("Got to view 1");
};
share|improve this answer
    
Is it possible to not do the script.onload an script.onerror here? I want to abstract the the script and document.getElementByTagName into a different method. – KVISH Aug 1 '15 at 2:03
    
Yes, you could instead have your script(viewn.js) call a method or trigger an event – Kevin B Aug 1 '15 at 2:23

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.