Gracefully handling AngularJS Error: $injector:nomod Module Unavailable

牧云@^-^@ 提交于 2019-12-03 13:42:13

I finally got it working. The solution requires a few hacks, though:

  1. checking if angular-google-analytics is loaded
  2. preparing the dependency list (deps) for the main module on the fly
  3. using $injector instead of Analytics explicitly

Still, I need to configure AnalyticsProvider, but it should be relatively easy to do with $injector.


var deps = [];

try {
  angular.module("angular-google-analytics"); // this throws if GA script is not loaded
  deps.push('angular-google-analytics');
} catch(e){ console.error("GA not available", e); }

angular.module('mainApp', deps)
.run(function($rootScope, $injector) {
  try {
    Analytics = $injector.get('Analytics');
    $rootScope.trackPage = function() {
      console.log('Analytics in action!');
      Analytics.trackPage();
    }
  } catch(e) {
    $rootScope.trackPage = function(key, label) {
      console.log("Fallback in action!")
    }
  }
})
.controller('MyCtrl', function($rootScope, $scope) {
  $scope.message = 'Hello World!';
  $rootScope.trackPage();
});

Updated plunker: http://plnkr.co/edit/Zo4RgKOybzhvJQdW2nQZ?p=preview

Since angular-google-analytics requires a config block to set the account, your best bet is to separate google analytics reporting into a separate sub-module of your App:

//create separate module for analytics reporting
var reportingModule = angular.module('mainApp.reporting', [
  'angular-google-analytics'
])
.config(function(AnalyticsProvider) {
  AnalyticsProvider.setAccount('UA-HELLO-GA');
})
.run(function(Analytics) {
  console.log('mmm.. analytics is good for you');
});

And then inject this sub-module into your main module asynchronously via the module.requires array so it runs after the main module has finished bootstrapping:

var mainApp = angular.module('mainApp', [

])
.controller('MyCtrl', function($scope) {
  $scope.message = 'Hello World!';
});

//set the dependency after app finished bootstrapping
setTimeout(function() {
  angular.module('mainApp').requires.push('myapp.reporting');
}, 1);

This way the main module finishes bootstrapping without having the reporting sub-module errors affecting it.

Here's a forked version of your plunkr: http://plnkr.co/edit/lgNZOz4MZx0FGoCOGRC9

Hashbrown

The accepted answer breaks gulp dependency injection so I iterated upon it to come up with a more self contained method (you'll still need to inject this module's script tag manually though if you are relying on gulp-inject).

You can add the dependency dynamically and you can avoid the use of $injector completely.

angular.module('app', [
    'your',
    'regular',
    'dependencies'
])
    .config(function(...) {
        /*your normal code*/
    })
    .run(function(...) {
        /*with no concern for analytics*/
    })
    //mimicking your answer for equivalence
    .controller('MyCtrl', function($rootScope) {
        if ($rootScope.Analytics)
            $rootScope.Analytics.trackPage();
    }) 
;


//protect against adblockers
try {
    //throws if not available
    angular.module('angular-google-analytics');

    //dynamically add the dependency
    angular.module('app').requires.push('angular-google-analytics');

    //set it up
    angular.module('app')
        .config(function (AnalyticsProvider) {
            AnalyticsProvider.setAccount('UA-00000000-0');
        })
        //so we dont need to use $injector
        .run(function(Analytics, $rootScope) {
            $rootScope.Analytics = Analytics;
        })
    ;
} catch(e) { console.error('GA not available'); }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!