Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I load angular modules, that are common and dynamic between pages?

Tags:

angularjs

I have a site that is developed by multiple developers that has multiple pages. Each "page" initializes angular by calling angular.module(etc).

My question is, all pages share some modules, and some pages use specific modules. What is the best practice to achieve this? Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics) or do I create one call that is shared my all pages that loads ALL the modules. And is there a way to do both? Such as, initilize the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.

like image 818
KingKongFrog Avatar asked Jun 19 '15 22:06

KingKongFrog


People also ask

Where can I import Angular modules?

Importing moduleslink When you use these Angular modules, import them in AppModule , or your feature module as appropriate, and list them in the @NgModule imports array. For example, in the basic application generated by the Angular CLI, BrowserModule is the first import at the top of the AppModule , app. module. ts .

Can an Angular app have multiple modules?

So now, you need to follow some simple steps to use multiple modules in an Angular application. Create a simple application using Angular CLI. If you are going to use Angular for the first time, click here. I am going to create an application which contains three modules: App, Employee, Admin, Home.


3 Answers

I have a site that is developed by multiple developers that has multiple pages. Each "page" initializes angular by calling angular.module(etc).

My question is:

  1. All pages share some modules, and some pages use specific modules. What is the best practice to achieve this?

I do not know the best practice when dealing with multiple pages. IMHO, creating multiple pages is a BAD practice nowadays. I think of web applications(SPAs) that have different views and states not web sites with disjointed pages. So if you choose to go the SPA(single page application) way, you can load all you core/common modules before the application bootstraps. Views/pages that need specific modules can lazy load them using something like oclazyload.

  1. Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics) or do I create one call that is shared my all pages that loads ALL the modules.

I can't say much about the question of trust as I do not know your developers well enough. In general, developers are never to be trusted, they will do anything that seems to work, high five themselves and call it a day. The idea is to "Trust but verify", you don't have to wire tap their phones or read their emails but never ever take your eyes off the main git or svn repository. Anywhere, If you were to use oclazyload as I suggested above for a SPA, you would only need to worry about dynamically loading 'view/page' specific modules which the developers can configure themselves.

  1. And is there a way to do both? Such as, initilize the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.

Yes, yes, there is ... SPA approach that I have already outlined above. I would also recommend using angular-ui-router for the states and views approach. The idea is to design your application whilst thinking of it as a desktop or mobile thick client that has state transitions and so forth.

like image 27
Alappin Avatar answered Sep 24 '22 06:09

Alappin


I would make one global module that is loaded by each individual app, much like modules like 'ngAnimate' are loaded... the global module could then initialize functionality common to all pages, such as Google analytics.

This requires some policing on all developers involved, but this is a good practice via code reviews, etc.

example page:

angular.module('individualPage', [
    'globalModule',
    'customPageModule'
]).config(
    // etc
);

global module:

angular.module('globalModule', [
    'googleAnalytics'
]).config(
    // etc
);
like image 168
stephenspann Avatar answered Sep 21 '22 06:09

stephenspann


I have a site that is developed by multiple developers that has multiple pages.

Each "page" initialises angular by calling angular.module(etc).

My question is, all pages share some modules, and some pages use specific modules.

What is the best practice to achieve this?

Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics) or do I create one call that is shared my all pages that loads ALL the modules.

And is there a way to do both? Such as, initialise the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.


I would start off by defining what a page is.

Are you talking about a SPA or a more traditional setup a la client transitions between pages with a regular <a href="/page"> and the server serves the client a piece of HTML?

If the latter is true, then I would urge you to reconsider your underlying approach to your Angular application.

The best (or rather, preferred) way of doing things would be to serve the client a single piece of HTML, and then transition between pages (from now on I will refer to them as states), by using either ngRoute, angular router (2), or better yet - ui-router.


For the remainder of my answer I am going to assume that you are in fact working with a SPA.


What is the best practice to achieve this?

As it stands, I would go out on a limb and say that there is no best practice defined for the case you present. There are a ton of ways to do it, none of which have been officially recommended by the core development team / community standard as far as I'm concerned.

You could go with webpack-angularjs-lazyload, requirejs (angular-requirejs-seed), requirejs (angularAMD), SystemJS among others. Pick your poison of preference!

Do I trust that developers will insert the correct modules that will be needed across the site (i.e. Google Analytics) or do I create one call that is shared my all pages that loads ALL the modules.

If code contained in an angular.module is required across the site, I would attach it to the main application module.

Such as:

/** Define core functionality that _is_ essential to the application running as expected **/
angular.module('core-module', [ 'route-definitions', 'http-interceptors', 'google-analytics' ]);

/** Inject the core functionality into a bundle module **/
angular.module('main-bundle-module', [ 'core-module' ]);

/** Bootstrap the bundle module as your application **/
angular.bootstrap(/* DOM element */, ['main-bundle-module']);

Now, whenever someone creates a new module for a specific state, they will need to inject said module into the main-bundle-module (barring lazy loaded modules). As such, the core functionality will always be supplied and available, in your case Google Analytics. In a sense, you just tore down the trust barrier.


Taking a step back, lets for a moment assume that you are not working with a SPA - and you are in fact re-initialising the angular application on each page transition (bad move). How would you ensure that the required functionality is always present?

Decorating the angular.module method.

Note: This is not officially supported, be wary of what you are doing. Also it defeats the purpose of modularisation in my opinion, but I'll showcase the way(s) of doing it.

You could go two ways here I reckon:

  • Kill the execution of JS if the required module is not a part of the developers module definition.
    • "Bad cop."
    • This would catch the 'untrusted developer' in his/her tracks during development, so as to ensure they are following the project standard.
  • Assist the 'untrusted developer' by automating the task of requiring the module.
    • "Good cop." (well, sort of...)
    • This would ensure that the required module is always present, albeit in every module.

"Good Cop"

(function(angular) {

  // The always required module(s).
  var ALWAYS_REQUIRED = ['cs.core'];
  // Keep a reference to the original angular.module function.
  var originalFn      = angular.module;
  // Keep track of registered modules.
  var registered      = {};

  angular.module = function (name, dependencies, configFunction) {
    var loaded;

    // Ensure that we are always working with an array of dependencies.
    dependencies = dependencies || [];

    // If the module has not already been registered
    if (!registered[name]) {

      // Ensure that the required modules are available.
      ALWAYS_REQUIRED.forEach(function (required) {
        if (dependencies.indexOf(required) === -1) {
          dependencies.push(required);
        }
      });

      // Register the module and store it in the registered object for future reference.
      loaded = registered[name] = original(name, dependencies, configFunction);
    } else {
      // Do not re-register the module, simply load it as per 'angular.module('name_of_module')';
      loaded = original(name);
    }

    // Return the loaded module.
    return loaded;
  };

})(angular);

"Bad Cop"

(function(angular) {

  var ALWAYS_REQUIRED = ['cs.core'];
  var originalFn      = angular.module;

  angular.module = function (name, dependencies, configFunction) {

    ALWAYS_REQUIRED.forEach(function (required) {
      if (dependencies.indexOf(required) === -1) {
        throw new Error('You need to add ' + required + ' to ' + name + '\'s module dependencies!');
      }
    });

    return originalFn(name, dependencies, configFunction);
  };

})(angular);

That's two ways of killing the trust issue, but in doing so we've introduced code that;

  • Is not very pretty.
  • Is definitely not very well tested / battle proven.
  • Kills modularisation to boot.

And is there a way to do both? Such as, initialise the modules that are needed across all the pages and then, load specific modules dynamically on their respective pages.

I would say the best way to do so is to:

  1. Take the steps necessary to convert to a SPA.
  2. Write some documentation for all your developers so as to bring them up to speed on the requirements of the project.
  3. Create a standalone module containing the required core functionality and attach it to your ng-app/angular.bootstrap module.
  4. Get ui-router, ui-router-extras and ocLazyLoad to allow for lazy loaded module/state/component definitions.
  5. Have a look at some of the following links for inspiration/ideas on what fits your specific project:
    • ocLazyLoad-SystemJS-Router by @lookfirst
    • ng-jspm-seed#futureStateConfig by @kasperlewau
    • ocLazyLoad#with-your-router
    • ocLazyLoad+requirejs plunker
    • webpack vs browserify @stackoverflow
    • angular + webpack slides

tl;dr

  • Convert to a SPA.
  • Bootstrap the application with core functionality supplied.
  • Write some documentation for untrusted developers.
    • Better yet, build trust. :)
  • Lazy load state-specific modules when needed.
like image 24
Kasper Lewau Avatar answered Sep 21 '22 06:09

Kasper Lewau