Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gracefully handling AngularJS Error: $injector:nomod Module Unavailable

My angular app depends on a third-party angular service:

var app = angular.module("ninjaModule", ['angular-google-analytics']);

The app loads up just fine, as long as my ad-blocking plugins are off. However, with ad-blockers on angular throws an $injector:nomod error, failing to load the whole app.

I'm looking for a way to gracefully handle these errors, and therefore be able to load the app regardless of ad-blockers. If angular-google-analytics won't be there - fine, it's not critical, I can deal with it or set up some fallback. But a situation where the whole app crashes is not an option for me. Any ideas?

To be precise - I don't want to work around ad-blockers, e.g. by renaming my script files. I'd expect an angular try-catch magic trick.

Plunker: http://plnkr.co/edit/sbEG6vclPidPSNGV5Bsa

like image 970
Paweł Rychlik Avatar asked Sep 22 '15 23:09

Paweł Rychlik


2 Answers

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

like image 65
Paweł Rychlik Avatar answered Sep 18 '22 03:09

Paweł Rychlik


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

like image 33
yvesmancera Avatar answered Sep 21 '22 03:09

yvesmancera