Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why create multiple AngularJS modules?

Tags:

angularjs

I'm so confused. AngularJS requires services, controllers, and directives to be created within Modules. But those services, controllers, and directives can be injected into any other service, controller, or directive REGARDLESS of the module they exist within! Correct? And, I haven't seen any IDEs or tools that care about the module names... other than ng-app. So, WHY create multiple modules? Does creating modules actually have a benefit other than read-ability within the source code?

Example...

(function(module) {

    var dir = function(something) {
        return {
        };
    };

    module.directive("myDirective", dir);

}(angular.module("pointlessModuleName")));

I've tried finding the answer, but keep getting confused...
Multiple Module in Angularjs

like image 294
G. Deward Avatar asked Apr 09 '15 14:04

G. Deward


1 Answers

I was thinking the exact same thing, and decided to walk on the wild side and merge everything under one module. Of course, only to find out a few minutes later why that was not a great idea :) So I'd like to take the time to explain where I think modules play a very nice role, independently from "the general opinion".

I started out with something along the lines of:

// main.js - v0

var ServerData = require('./services/server-data')
var filters = require('./filters')

angular.module('ServerLoad', ['ngRoute'])
.factory('ServerData', ['$http', ServerData])
.filter('lastseen', function () {return filters.lastseen})
.config(['$routeProvider', function ($routeProvider) {
  $routeProvider
  .otherwise({
    redirectTo: '/servers'
  })
}])

Now I wanted to add a view that presents a list of servers. A view, in essence, is not much more than a combination of a controller with a route, so I added those to the code. Here is what I got (and then I'll explain why this is not so nice and how a sub module solves that):

// main.js - v1

var ServerListView = require('./views/server-list-view')       // <-- import view
var ServerData = require('./services/server-data')
var filters = require('./filters')

angular.module('ServerLoad', ['ngRoute'])
.factory('ServerData', ['$http', ServerData])
.controller('ServerListCtrl', ['ServerData', ServerListView])  // <-- add controller
.filter('lastseen', function () {return filters.lastseen})
.config(['$routeProvider', function ($routeProvider) {
  $routeProvider
  .when('/servers', {                                          // <-- add route
    controller: 'ServerListCtrl',                              //
    controllerAs: 'servers',                                   //
    templateUrl: 'assets/partials/server-list.html'            //
  })                                                           //
  .otherwise({
    redirectTo: '/servers'
  })
}])

Although this is certainly a valid set-up, what I don't like about it is that main.js defines the controllerAs and the templateUrl properties. To me it feels these properties really ought to be defined where the code that depends on that is defined --- './views/server-list-view', maybe even on the ServerListView class itself.

So, a bit in the same vein as your IIFE, I added a static method to ServerListView to add the view to the module:

// main.js - v2 (from v0)

var ServerListView = require('./views/server-list-view')       // <-- import view
// ...

angular.module('ServerLoad', ['ngRoute'])
// ...

ServerListView.setup(window.angular.module('ServerLoad'))      // <-- adds ctrl and route

and

// server-list-view.js - v2

function ServerListView (ServerData) {
  var scope = this
  // ...
}
module.exports = ServerListView

ServerListView.setup = function (module) {
  module
  .controller('ServerListCtrl', ['ServerData', ServerListView])
  .config(['$routeProvider', function ($routeProvider) {
    $routeProvider
    .when('/servers', {
      controller: 'ServerListCtrl',
      controllerAs: 'servers',
      templateUrl: 'assets/partials/server-list.html'
    })
  }])
}

I thought this looked slightly better, but now the use of 'ServerData' bothered me. After all, it wasn't declared in server-list-view.js anywhere. Similarly, if the view would need some extra external module, I would have to edit main.js to add it. That feels out of place.

Then it occurred to me that, if I can write an IIFE to take module as argument, why not just let it take angular as argument? Then it can define its own module however it pleases and include all dependencies needed. Moreover, if you're still fanatical about single-module-everything, then just use the 'ServerLoad' module name and drop the dependency declaration. That is the only and whole difference!

// server-list-view.js - v3

// ...

ServerListView.setup = function (angular) {
  angular.module('ServerLoad.ServerListView', ['ngRoute', 'ServerLoad'])
  // ...
}

So, in conclusion, I think that the great advantage of using sub modules is not necessarily that others can reuse my code (I don't think anybody will ever be interested in my server list view), but that each bit of code can stand on its own, reusing other people's code, without requiring the main module to include all of that. The main.js stays lean while the server-list-view.js can pull in all sorts of stuff.

foot note. It still looks a bit odd having ServerLoad as a dependency (to reach the ServerData factory). In version 4 that factory will also live in its own sub module, of course ;-)

like image 70
Paul Avatar answered Oct 06 '22 04:10

Paul