How to check for the existence of a module without an error being raised?
Asked Answered
M

7

44

In Angular 1.2, ngRoute is a separate module so you can use other community routers like ui.router instead.

I'm writing an open-source module that aims to work for multiple different router implementations. So how can I check which router is loaded or exists?

I'm doing the following inside a factory in my module, but it does not work the way I expect it to:

if (angular.module("ngRoute"))
  // Do ngRoute-specific stuff.
else if (angular.module("ui.router"))
  // Do ui.router-specific stuff.

It raises an error for whichever module is not loaded. For example, if the app is using ui.router, then the following error is raised for the ngRoute check:

Uncaught Error: [$injector:nomod] Module 'ngRoute' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.

Marianamariand answered 6/10, 2013 at 7:39 Comment(1)
I don't know if there is a "right" way of doing it, but I can think of a work-around at least. You could do try { angular.module('ngRoute') } catch(e) { //Here you know it's not there } but there's probably a better way.Crossruff
A
57

I am not aware of a way of checking without an error being raised; however, notice that the issue is that it was an Uncaught Error, not that an error was thrown. The pattern for catching such an error is the following.

try { angular.module("ngRoute") } catch(err) { /* failed to require */ }

If an error is caught, you can try the other module, and if not, you can use the first.

If your behavior will be the same for each module, you could do something like the following, in which we define a function which will attempt the first of the listed module names, and if an error is thrown, try the next option.

var tryModules = function(names) {
  // accepts a list of module names and
  // attempts to load them, in order.

  // if no options remain, throw an error.
  if( names.length == 0 ) {
    throw new Error("None of the modules could be loaded.");
  }

  // attempt to load the module into m
  var m;
  try {
    m = angular.module(names[0])
  } catch(err) {
    m = null;
  }

  // if it could not be loaded, try the rest of
  // the options. if it was, return it.
  if( m == null ) return tryModules(names.slice(1));
  else return m;
};

tryModules(["ngRoute", "ui.router"]);
Adjectival answered 6/10, 2013 at 8:11 Comment(1)
Here m = angular.module("ngRoute"); need to change as m = angular.module(names[0]);Karr
P
9

I would test for the service instead of the module itself.

// In controller
if($injector.has('$route')){

}
if($injector.has('$state')){

}

// In angular config
if($injector.has('$routeProvider')){

}
if($injector.has('$stateProvider')){

}
Protomorphic answered 20/10, 2015 at 20:22 Comment(0)
L
6

The original answer is legit. However, as an alternative, I wrote this when I needed to "find or create" the modules. There's a number of use cases, but generally, it lets you not have to worry about file load order. You could either put this in a initialModules.js... or the top of all your individual service/directive files start with something like this. This little function works like a charm for me:

var initialModules = [
  {name: 'app.directives', deps: ['ui.mask']},
  {name: 'app.services'},
  {name: 'app.templates'},
  {name: 'app.controllers'}
];

initialModules.forEach(function(moduleDefinition) {
  findOrCreateModule(moduleDefinition.name, moduleDefinition.deps);
});

function findOrCreateModule(moduleName, deps) {
  deps = deps || [];
  try {
    angular.module(moduleName);
  } catch (error) {
    angular.module(moduleName, deps);
  }
}


///// OR... in like "myDirective.js"
findOrCreateModule('app.directives').directive('myDirective', myDirectiveFunction);
Lynx answered 11/8, 2015 at 23:25 Comment(1)
Tried to find a way to send you a message, but Im not too used to SO I guess. This was a really nice solution to the problem. I was just about to write a try-catch on top of all my files, but I think I will go with this solution instead, defining all modules in my app.js. Well coded!Taphouse
V
1

If you decorate angular.module to store the names in an array then you could just check if the array contains your module name.

Decorate angular.module

See @dsfq's answer on SO.

This needs to happen after angular is loaded but before you start loading any angular modules.

Check for your module

if(angular.modules.indexOf("ngRoute") > -1) ...

Varityper answered 1/7, 2016 at 9:29 Comment(0)
G
0

The problem of automatically load or create a module could be better solved by something like gulp-angular-filesort, though. It works really flawlessly.

From gulp-angular-filesort github page: Automatically sort AngularJS app files depending on module definitions and usage

Used in conjunction with gulp-inject to inject your AngularJS application files (scripts) in a correct order, to get rid of all Uncaught Error: [$injector:modulerr].

Disclaimer: I'm not affiliated with gulp-angular-filesort, I only use it with a lot of profit.

Gabler answered 24/9, 2015 at 7:58 Comment(1)
Please add some content from the link.Towney
C
0

A much better solution is to simply do your check when the module is created. You just need a utility function to add a callback.

//create a utility function to add a callback to object methods    
//here we are making it a method of the underscore or lowdash object
//but it could be added to the angular global object or anything else
_.addCallBack = function (obj, originalMethodName, callBackMethod, context){
            var fnOriginal = obj[originalMethodName],
                outcome;

            context = context || obj;

            obj[originalMethodName] = function () {
                var outcome = fnOriginal.apply(this, arguments);

                callBackMethod.apply(this, arguments);

                return outcome;
            };
};

_.addCallBack(angular, "module", function(sModuleName, asDependencies){
    if(_.contains(asDependencies, "ngRoute")){
      //your logic here
      //just loop through if you don't use underscore or lowdash
    }
});
Chunky answered 24/3, 2016 at 18:31 Comment(0)
K
0

AngularJS 1.6.3 and up has a way to check if a module is loaded via the $injector service.

Also added in 1.6.7 was the ability to load new modules which may be of interest to some.

Kinescope answered 19/2, 2018 at 21:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.