1

As discussed here, IE is bad about caching things when using ajax (i.e., $route and $http). A brilliant solution to this problem can be found here.

We use many angular apps on our site, so in an effort to ensure the caching issue doesn't occur for other developers working on other angular apps, I would like to add the above proposed solution to every Angular app on our site as a matter of course. If our style guide included the following, a developer would be more likely to include it when building his own app, and would successfully avoid the very dangerous IE issue. After all, not many developers I know actively develop in IE anymore.

var myApp = angular.module('myApp', ['ngRoute']);

myApp.config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
    $httpProvider.defaults.cache = false;
    if (!$httpProvider.defaults.headers.get) {
      $httpProvider.defaults.headers.get = {};
    }
    // disable IE ajax request caching
    $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
    // routes go here...
}]);

What this code does is force IE not to cache templates and to issue server requests for $http calls instead of pulling the results from its cache (which can be really bad if your data is dynamic -- which it may very well be).

The problem with adding the code above for every Angular app is that the app may or may not be injecting ngRoute into the app's dependencies. If the ngRoute is not present, an injection error will occur.

So, the question is this: Is it possible to do a check for the presence of an injected dependency in Angular? I would love to be able to do something like the following:

var myapp = angular.module('ordersApp', [
    'ngTouch',
    'ngRoute',
    'ngAnimate',
    'myController'
]);

if(myapp.has_ngRoute) {
     myapp.config(['$routeProvider', '$httpProvider', function($routeProvider, $httpProvider) {
    $httpProvider.defaults.cache = false;
         if (!$httpProvider.defaults.headers.get) {
             $httpProvider.defaults.headers.get = {};
         }
         // disable IE ajax request caching
        $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
        // routes go here...
    }]);
}
Community
  • 1
  • 1
luvaas
  • 2,156
  • 1
  • 18
  • 14

2 Answers2

2

There are two ways to address this:

1. Check if a service exists and run behaviour based on that

This answers your specific question - only run code if a service exists

You can use $injector.has(...) to see if a provider/service has been registered.

You can check to see if $routeProvider has been registered and configure the HTTP headers if it does exist.

Here is an example where we check for $routeProvider before configuring the $httpProvider.

// Cache configuration behaviour.
var configureCache = ['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.cache = false;
        if (!$httpProvider.defaults.headers.get) {
            $httpProvider.defaults.headers.get = {};
        }
        // disable IE ajax request caching
        $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
}];


angular.module('testApp', [
        // Test this by commenting this out.
        'ngRoute'
    ])
    .config(['$injector', function($injector){
        if ($injector.has('$routeProvider')){
            // If $routeProvider has been registered then
            // configure the $httpProvider.
            console.log('Has $routeProvider: configuring cache.');

            // Use `$injector.invoke(...)` so we can use
            // Angular dependency injection in our
            // configuration function above.
            $injector.invoke(configureCache);
        } else {
            // Otherwise don't do anything.
            console.log('Skipping cache configuration.');
        }
}]);

Here is the example in a Plunker: http://plnkr.co/edit/orXOjMg0YZXDfwoqf9OD


2. Split your code into modules

Don't check if a module/service exists, use module dependencies to control this instead

With this method we get more flexibility. You define the caching behaviour in its own module that has a dependency on ngRoute.

That way when you include your cache customisation behaviour you know ngRoute must exist because it's one of the dependencies!

This gives you three ways of controlling the routing/caching behaviour in your application depending only on which modules you include:

No routes

angular.module('testApp', [
        // Don't add 'ngRoute' module dependency here
        // or alter the caching behaviour.
    ])

Routes but with default caching behaviour

angular.module('testApp', [
        'ngRoute'
    ])

Routes with custom caching behaviour

angular.module('testApp', [
        'noCacheRoutes'
    ])

In each case we've only had to change the module dependencies but have changed the behaviour of the application in a known way.

Here is the example of splitting the cache configuration behaviour into a separate module:

/**
 * We decide that it only make sense to use this 
 * module along with `ngRoute` so define it as
 * a dependency here.
 */
angular.module('noCacheRoutes', [
        'ngRoute'
    ])
    .config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.cache = false;
        if (!$httpProvider.defaults.headers.get) {
            $httpProvider.defaults.headers.get = {};
        }
        // disable IE ajax request caching
        $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
    }]);


/**
 * Don't reference `ngRoute` here, let `noCacheRoutes`
 * bring it in as a transitive dependency.
 * 
 * If later on you decide you don't need to change
 * the caching behaviour you can just replace `noCacheRoutes`
 * with `ngRoute` and don't need to make any further changes.
 */
angular.module('testApp', [
        'noCacheRoutes'
    ])
    .config(['$routeProvider', function($routeProvider) {
        // Configure your application routes here.
    }]);
Sly_cardinal
  • 12,270
  • 5
  • 49
  • 50
  • This was a great help! Thanks for the very detailed and thorough reply, @Sly_cardinal. I'm going to go with your suggestion to split the code into modules. Seems like a better way to maintain flexibility as you suggest. – luvaas Jul 07 '15 at 05:44
0

As dicussed here AngularJs - get list of all registered modules, there is no method for getting module list, but you can extend angular's module method (see the accepted answer).

Community
  • 1
  • 1
rzelek
  • 3,975
  • 1
  • 33
  • 35