Search code examples
angularjsrequirejsamd

proper way to separate angular modules from AMD modules?


I am confused about the proper way to implement requireJS (and the r.js optimizer) into a very large AngularJS SPA. I would like some advice on how to organize the project so I get the benefit of AMD modularization, but not over complicate things (wrapping angular modules inside require js defined modules, which are all dumped into an optimized .js file...).

I have a few different categories of files I'm dealing with:

  1. I have a bundle of vendor libraries (jquery, angular, angular-resource, require, lodash, etc)
  2. I have some container level AMD modules (authentication, analytics, etc)
  3. I have lots of angular modules, services, filters, etc

Using r.js to concat and minify all the vendor stuff together seems like a no-brainer, but when I started adding in all the other code, I felt like my project got muddy. For example, I have an Auth service, a couple controllers, and my vendor dependencies (jquery...). So my main.js that require looks at is sorta like this:

require.config({
  paths: {
    // load vendor libraries
    'domReady':      '../vendor/requirejs-domready/domReady',
    'angular':       '../vendor/angular/angular',
    'jquery':        '../vendor/jquery/jquery.min',
    requireLib:      '../vendor/requirejs/require'
     ... lots of these

    //load app code
    'app':           '../app/app',
    'AuthCtrl':      '../app/modules/auth/AuthCtrl',
    'UserCtrl':      '../app/modules/auth/UserCtrl',
    'SettingsCtrl':  '../app/modules/auth/SettingsCtrl',
    // potentially dozens and dozens of these...

    //load auth library
    'auth':          '../app/modules/common/auth',
    'analytics'      '../analytics'

  },
  include: ['domReady', 'requireLib'],

  shim: {
    'angular': {
      exports: 'angular'
    }
  }
...
});

Currently, I have been dumping all of the code from all 3 'categories' above into 1 big uglified js file.

What I'm not happy with is I feel like my controller is starting to need a laundry list of module dependencies:

require('app',[
  'angular',
  'jquery',
  'AuthCtrl',
  'UserCtrl',
  'SettingsCtrl',
  ' ... potentially dozens? '
  ...
], function (angular, $, Auth, user, settings, ...potentially dozens?) {
  'use strict';

  return angular.module('ngAD', ['AuthCtrl', 'UserCtrl', 'SettingsCtrl' ...]);
});

So my question is two fold I guess:

  1. What is the benefit of keeping all my angular modules, controllers, filters, etc, defined in their own AMD modules and included manually via requirejs? They are all in memory already (having been loaded into the big pile-o-files via r.js). Is there a smarter way to handle all these discreet angular modules when there will be tons of them? Should I concat them all into their own module that can be a single injectable dependency?
  2. I'm getting mixed up between my "AMD modules" and "angular modules". Do all my angular components (controllers, directives, etc) need to be in their own AMD modules? I feel like I'm wrapping a module (angular) inside a module (require's AMD format).

Solution

  • There should be no need to do all of that.

    I used this as a starting point https://github.com/Matthew-Odette/angular-require-bootstrap-seed

    Then all that is required is to add controllers and services to the require section at the end of the AMD file. e.g.

    require(
        [
        // Dependencies from lib
                'angular', 
                'ngRoute',
                '../lib/less.min.1.5.0',  
        // Angular directives/controllers/services
                'app',
                'core/viewHomeController',
                'core/commonRoutes',
                'core/header',
                'events/events-ctrl
        ], 
        function (angular) {
                var AppRoot = angular.element(document.getElementById('ng-app'));
                AppRoot.attr('ng-controller','AppCtrl');
                angular.bootstrap(document, ['TheApp']);
        });
    

    events-ctrl.js being new controller/service that's been added, further controllers/services would be added the same way

    Then the controllers/services needs to be wrapped in require code e.g. this is beginning of events-ctrl.js

    define(['app'], function (app) {
    
      app.factory('EventService', function($resource) {
        return $resource('/api/events/:id');
      });
    
      app.controller('EventListCtrl', ['$scope', '$modal', 'EventService', function($scope, $modal, EventService) {
        console.log('EventListCtrl working');
        ...