Ruben.Canton
Ruben.Canton

Reputation: 1303

Angular scripts loading: how to include all scripts in just one call

I was trying to create a script that would include all the required ones to make my Angular app work but doesn't seem to work:

function include(destination) {
    var e = window.document.createElement('script');
    e.setAttribute('src', destination);
    window.document.body.appendChild(e);
}

include('../Scripts/myApp/main.js');
include('../Scripts/myApp/appConfig.js');
include('../Scripts/myApp/MyAppController.js');
include('../Scripts/myApp/Service1.js');
include('../Scripts/myApp/Service2.js');

Unfortunately, if I try to include the app scripts like that (it works btw, they are loaded into the page perfectly), I get an error as angular detects my "ng-app='myAppName'" directive but finds no module declared for it (I suppose because the script hasn't been loaded yet when angular checks). If I then try to at least place the module instantiation on a script called directly by the HTML page (the main.js) then it complains for the controller, services, etc. not existing and if I attach those to the app inside their own files (app.controller(), app.factory, ...) it does not complain but it doesn't happen either.

Looks like loading the scripts this way makes Angular not execute them as they may be loaded too late or something. The only way of doing it has been to set a script tag calling the scripts on the HTML.

But I have the feeling that I'm missing something, that there is a way of doing this and I just don't see it.

Does anyone know how to load all the required scripts by just calling one of them? Is there something on the angular app config that can be set to do so maybe?

It just looks too dirty to make so many calls and also bad in terms of performance.

I've checked that there is something called Yeoman but it looks like too big for what I want, then I saw RequireJS but I'm unsure that's what I really want and again not sure I want to install another library just for this. Finally, I've seen that on the app config you can set where to find the views and even a way to load the controller (I think, I'm unsure of anything now my head is so dizzy) so I was thinking that maybe Angular has already something to do this which looks so natural, joining the scripts into one?

Many thanks.

Upvotes: 1

Views: 1798

Answers (2)

Patrick
Patrick

Reputation: 6958

This is because your scripts are being loaded in asynchronously and angular is compiling the document synchronously as the page is loaded (since I'm assumnig angular.js is on the script includes as I don't see it above). You will have to manually bootstrap the app in this scenario, as you would also have to do when using RequireJS. Here is an example... Since you are not using a library like RequireJS, I am also putting a hacky counter to see when all your scripts are loaded to callback and bootstrap angular when all scripts have loaded:

function include(destination) {
    var e = window.document.createElement('script');
    e.setAttribute('src', destination);
    //IE detect when script has fully loaded... Increase number of loaded scripts and
    //call bootstrap if all have loaded.
    e.onreadystatechange = function () {
        if (newjs.readyState === "loaded" || newjs.readyState === "complete") {
            e.onreadystatechange = null;
            loadedScripts++;
        }

        if (loadedScripts === numScripts) {
            bootstrapAngular("myAppName");
        }
    }

    //Other browsers. Same logic as above - Increase scripts loaded and call bootstrap when complete
    e.onload = function () {
        loadedScripts++;
        if (loadedScripts === numScripts) {
            bootstrapAngular("myAppName");
        }
    }
    window.document.body.appendChild(e);
}

//5 scripts being loaded
var numScripts = 5;
//0 currently loaded
var loadedScripts = 0;
include('../Scripts/myApp/main.js');
include('../Scripts/myApp/appConfig.js');
include('../Scripts/myApp/MyAppController.js');
include('../Scripts/myApp/Service1.js');
include('../Scripts/myApp/Service2.js');

function bootstrapAngular(appName) {
    //Assuming you are putting ng-app at document level... Change to be your specific element.
    angular.bootstrap(document, [appName]);
}

The solution above is a little hacky as we are keeping track of all the scripts that have loaded. This is where a library like RequireJS would play very nicely because it would call your callback function when the scripts you want have been loaded. An example would be:

require(['angular', 'jquery', 'appConfig', 'MyAppController', 'Service1', 'Service2'], function (ng, $) {
    //bootstrap when all is ready
    $(function () {
        ng.bootstrap(document, ["myAppName"]);
    });
});

Upvotes: 3

Jordan Running
Jordan Running

Reputation: 106087

If you're using AngularJS (or any similarly advanced framework) then it's definitely time for you to learn about module loading. Injecting <script> tags quickly becomes untenable as you start to build more modules and start dealing with more complex dependency chains.

RequireJS is a great package and can do everything under the sun. Here's a tutorial on using the two together that looks pretty good. Browserify is a similar library that lets you manage dependencies with NPM. It's a little bit simpler than RequireJS, which might be a plus for you. Here's a tutorial that looks decent.

Both RequireJS and Browserify come with command-line build tools, which are great because they can combine all of your source code into a single file when you're ready to deploy your app to a server.

I should warn you, however, that this stuff isn't easy. There's a fairly steep learning curve. In all likelihood, though, this is stuff you're going to need to learn sooner or later anyway.

Upvotes: 2

Related Questions