Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS RequireJS Browserify and the Javascript module/global scope nightmare [closed]

I have been digging a bit lately in all this CommonJS vs AMD battle and this is my findings... ( BTW I am not preaching here I am sharing my thoughts to get some constructive insights... ) The RequireJS brings to much complexity to my Angular modules, it feel wrong to me as it's a module wrap in a module... The Browserify way is cleaner but for it to work correctly with every thing, you need to have all your dependency and your dependency-dependencies implemented correctly and unfortunately we don't live in a perfect world... so you have to shim and shim the inner dependency of the shimmed Libs... I am not a big fan of extra complexity...

The way I am currently going for ( and subject to all your constructive criticism... )

I have a grunt file concatenating and minify all my resources, the libs like BreezeJS, $, Q, Ladda, etc... leaks on the global scope... Then I declare this type module for these globals:

module.value('gLadda', (function(){

    if("Ladda" in window && "Spinner" in window){
        return Ladda;
    }else{
        throw new Error("The Globals Ladda || Spinner are missing");
    }

})());

After in my app I work with the "Angularify" dependencies, I haven't used this technique in a team and I am wondering if this is firing some red lights for some, and if they would explain why... Thank you for your time.

like image 648
Brett Avatar asked Nov 14 '13 20:11

Brett


1 Answers

From a feature request I have for inclusion of an AMD loader as part of the additional ng-modules.

Angular comes with a feature that allows users to create Module definitions and look up entities such as controllers later by a String Identifier. Angular does not come with the ability to load scripts stored in a separate file which means that you are left with four options:

  1. Store all your javascript in a single file.
  2. Separate your javascript into separate files and add script tags for each file.
  3. Use an AMD file loader like requireJS to manage script files and their dependencies.
  4. Use a precompiler like browserify to merge nodejs style files into a single script file.

As a project grows, the files become more difficult to manage, not just because of their size, but because the dependencies between modules become more complicated. Large projects can also benefit from the lazy loading of an AMD loader.

An AMD module loader lists the dependencies that need to be present before this file can be run. The problem is that AMD dependencies are close to the Injection list used by Angular but not exactly the same thing. One problem can be seen in this code:

define(['angular'], function(angular) {
  return angular.module('myApp.controllers', ['myApp.services'])
    .controller('MyController', ['$scope', 'myService',
      function($scope, myService) {
        $scope.data = myService.getData();
      }
    ])
};

The define statement at the top says to make sure that angular is initialized before running this function. The angular.module statement says to lookup '$scope' and 'myService' and inject them into the variables $scope and myService. The problem is that the AMD loader might not have initialized the file that defines myApp.services, in which you can presume is defined myService, because it did not appear in the define statement above. This creates a huge disconnect between the injection list and AMD dependency list. Not only is it difficult to tell if 'myApp.services; has been loaded, it is also difficult to tell what particular components are available in 'myApp.services'. IOW Is 'myService' both loaded and injectable?

I currently use option #3 in the form of requirejs because I need to have the ability to dynamically load controllers via routing (See the first link). Also, the application's size I am working with now makes browserfy impractical since there a so many pages. I do, however, agree that this is sub-optimal but I don't see any other choice at the moment.
From a practical standpoint, I define one injectable per file. I also try to make all injectable arrays match the require array. This isn't necessary but it makes the code more maintainable.

I found these articles helpful in writing this:

https://github.com/marcoslin/angularAMD

http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx

like image 134
Nathaniel Johnson Avatar answered Sep 27 '22 18:09

Nathaniel Johnson