2

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.

9
  • 1
    you forgot to return a promise that gets resolved when the controller has been loaded to your resolve function. Commented Jul 31, 2015 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. Commented Jul 31, 2015 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. Commented Jul 31, 2015 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. Commented Jul 31, 2015 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. Commented Jul 31, 2015 at 20:11

1 Answer 1

1

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

2 Comments

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.
Yes, you could instead have your script(viewn.js) call a method or trigger an event

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.