How to modularize AngularJS applications / plugins How to modularize AngularJS applications / plugins angularjs angularjs

How to modularize AngularJS applications / plugins


** This answer is incomplete **

I want to make sure I understand you before I dig into this.

Here's a quick implementation of a Loader module to manage lazy loading (plugins, vendor assets, admin stuff etc).

Does this help?

angular.module('Bizcoin.loader').service('Loader', Loader);function Loader($injector, $ocLazyLoad, User) {  var Loader = {    load: load,    get: get,    plugins: {},    flags: {}  };  init();  return Loader;  function init() {    load('vendors');    if (userIsAdmin)      load('admin');  }  function load(plugin) {    Loader.plugins[plugin] = Loader[plugin] || $ocLazyLoad.load('path/to/'+plugin+'.js');    return Loader.plugins[plugin].then(setFlag);    function setFlag() {      return Loader.flags[plugin] = true;    }  }  function get(plugin) {    return load(plugin).then(function() {      return $injector.get(plugin);    });  }}


I work on an large .Net/AngularJS application that is composed of 20+, independent, Areas (or modules) and some core functionality common and reused across all Areas.

Let me go into detail on how I do this for my particular case, and it might give some ideas. The fact that I use .Net is irrelevant here, since this can achieve with any framework.

Each Area acts as an independent application that depends only on the core functionality, always present. Each Area has its own ASP.Net MVC route. Each Area registers with the core application the menu links it wants to provide.When the customer goes to the application dashboard, only the core part of of the application. When the user clicks on link in the menu, it will navigate to the content provided by one of the Areas, and only the core plus the assets of that Area are loaded.

Lets see how this is done.

In the main page of the application I load the scripts like this:

<script type="text/javascript">    // a JS object with all the necessary app data from the server.    // e.g.: menu data, etc    window.appContext = @Html.Action("ApplicationContext", "Portal")); </script>@Scripts.Render("~/bundles/angular-main")@RenderSection("AngularAreas", required: false)

I make use of dot .Net bundles and sections.

The main (core) AngularJS part of the application consists of angular configuration, internationalization services, global notifications service, reusable UI components, etc. This is loaded is @Scripts.Render("~/bundles/angular-main").

The @RenderSection("AngularAreas", required: false) section will be filled in by each area when the user navigates to that Area.

Lets see some AngularJS code.Here is part of the main app.ts.

// If user is visiting an Area, the NgModules array will be augmented.// with the modules the Area wants to provide (to be bootstrapped)export var LoadNgModules = [    NgModules.Config,    NgModules.Core];angular.module(NgModules.Bootstraper, LoadNgModules);angular.element(document).ready(function () {    angular.bootstrap(document, [NgModules.Bootstraper]);});

Lets look at an example Area now.

And here is how an Area would supply its assets, to be outputted in @RenderSection("AngularAreas", required: false):

@section AngularAreas {    @Scripts.Render("~/bundles/areas/staff-management")}

Its a simple bundle containing all the scripts for that Area.Now, let's see the important part of the AngularJS code for this Area.

var StaffManagementNgModule = 'areas.staff-management';// Push our self into the modules to be bootstrappedLoadNgModules.push(StaffManagementNgModule );// Define the moduleangular    .module(StaffManagementNgModule , ['ngRoute', NgModules.Core])    .config([        '$routeProvider', '$locationProvider', ($routeProvider: ng.route.IRouteProvider, $locationProvider) => {            $routeProvider                .when(staff', { template: '<staff></staff>' })                .when('staff/details/:id', { template: '<staff-details></staff-details>' });        }    ]);;

That is all, from here the Area is a normal Angular application.

To summarize, we load the main (core) AngularJS functionality and provide the LoadNgModules array that an area can fill with its own modules.

we load the Area scripts and and "our self" to the LoadNgModules array.

finally the run angular.bootstrap.

For completeness, here is a fragment of C# showing how an area would indicate to the main application that it is available

public class ItemManagementModuleRegistration : IModuleRegistration{    public void Register(ModuleContext moduleContext)    {        string thisAreaName = "Staff";        moduleContext.RegisterMenu(menuContext =>        {            var ItemsMenu = menuContext.Items(thisAreaName);            // add urls and stuff...        });        // register more stuff with the moduleContext    }}

Using reflection one can easily find what areas are "installed".

These are the main moving parts of the setup.Each Area can have its own API and tests. It is quite flexible.