1

I am just learning Angular and I have some questions regarding the architecture of my app.

The project I will be working on will be using allot of external libraries: jQuery, jQuery.ui, jsPlumb and so on, with different loading times.

I know that each code related to these libraries will have to be handled inside directives.

I worked with Backbone which uses Require JS effectively - on each view, I could set what libraries I need and the view would be loaded as soon as those libraries are available.

Now, on angular side - what would be the correct way of handling this issue?

From the top of my head, I am thinking of:

  1. Place checks inside the router - checking if the desired libraries for a certain route are loaded.

  2. Place checks inside each directive - for example if one directive uses jsPlumb, place a check inside and return the directives content when that library is loaded - I believe this could generate problems when interacting with other directives on the same view, which require multiple libraries with different loading times.

  3. Load angular only after every other library is loaded - that would lead to long loading times.

What's the best way to handle all those issues?

3 Answers 3

3

You can create a factory to load the external library you need. Return a deferred object for the library's script after it loads. Here is one I used for d3 library:

var d3js = angular.module('d3', []);

d3js.factory('d3Service', ['$document', '$q', '$rootScope', '$window',
    function($document, $q, $rootScope, $window) {
        var d = $q.defer();

        function onScriptLoad() {
            // Load client in the browser
            $rootScope.$apply(function() { d.resolve($window.d3); });
        }
        // Create a script tag with d3 as the source
        // and call our onScriptLoad callback when it
        // has been loaded

        var scriptTag = $document[0].createElement('script');
        scriptTag.type = 'text/javascript';
        scriptTag.async = true;
        scriptTag.src = 'lib/d3.v3.js';

        scriptTag.onreadystatechange = function () {
            if (this.readyState == 'complete') onScriptLoad();
        }

        scriptTag.onload = onScriptLoad;

        var s = $document[0].getElementsByTagName('body')[0];
        s.appendChild(scriptTag);

        return {
            d3: function() { return d.promise; }
        };

    }]);

then in your directive, use then function of the deferred to wait until it's ready

d3Service.d3().then(function(d3) {
    // place code using d3 library here
}

If your directive is needing access to multiple libraries, you can chain the deferreds.

d3Service.d3().then(function(d3) {
    someOtherLibSvc.getLib().then(function(lib){
        // place code using d3 library and someOtherLibrary here
    }
}

To avoid this chaining check out bluebird and use Promise.join, Angular comes with $q automatically so I just used that here.

Note: if you just load JQuery before angular, then angular.element will already reference JQuery. So you don't need to do this for JQuery, just load it in your main html page before Angular

Sign up to request clarification or add additional context in comments.

1 Comment

Or you can read the article that this was pasted from - ng-newsletter.com/posts/d3-on-angular.html
1

In short go http://slides.com/thomasburleson/using-requirejs-with-angularjs#/

route.

However now suffer having to put the defines and requires everywhere. For a large application this may get messy if not planned carefully. Use grunt requirejs plugin for builds because without this things would be wash.

Comments

0

I'm not sure this is the "correct" way to do this, but here is perhaps a simpler way to handle external libraries (in this case d3.js) within Angular 1.x code.

This is the same basic idea as @aarosil's answer (use a factory), but using fewer dependencies on other services - for what that's worth.

var app = angular.module('SomeApp', []);
app.factory('LibraryFactory', function () {
    var factory = {};

    factory.getD3 = function(callback) {
        if(!window.d3) {
            var script = document.createElement("script");
            script.src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.9/d3.min.js";
            document.head.appendChild(script);
            script.onload = function() {
                callback(window.d3);
            };
        } else {
            callback(window.d3);
        }
    };

    //factory.getSomeOtherLibrary = ...ditto

    return factory;
});

And to use the factory (eg, in a controller):

app.controller('SomeTrialWithD3Ctrl', function ($scope, LibraryFactory) {

    LibraryFactory.getD3(function main(d3) {
        // place code using d3 library here
    });
});

And of course, the callbacks can be chained if you need multiple libraries at once.

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.