/angular-deferred-bootstrap

Initialize your AngularJS app with constants loaded from the back-end.

Primary LanguageJavaScriptMIT LicenseMIT

angular-deferred-bootstrap

Build Status Coverage StatusNPM version ![Gitter](https://badges.gitter.im/Join Chat.svg)

Initialize your AngularJS app with constants loaded from the back-end.

This component provides a global resolve function for your app. It works similar to the resolve functions you may know from ngRoute or ui-router: You define what needs to be loaded from the back-end before your application can be started and the deferred bootstrapper takes care of loading the data and bootstrapping the application.

Install

bower install --save angular-deferred-bootstrap
npm install angular-deferred-bootstrap
jspm install angular-deferred-bootstrap

Usage

Instead of using the ng-app directive or angular.bootstrap(), use the deferredBootstrapper to initialize your app:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  resolve: {
    APP_CONFIG: ['$http', function ($http) {
      return $http.get('/api/demo-config');
    }]
  }
});

This will make the response of your $http call available as a constant to your AngularJS app. This means you can now use dependency injection to access the APP_CONFIG wherever you need it in your app (even in config() blocks!).

angular.module('MyApp', [])
  .config(function (APP_CONFIG) {
    console.log('APP_CONFIG is: ' + JSON.stringify(APP_CONFIG));
  })

Loading and error indicators

To make it possible to conditionally show a loading indicator or an error message when the initialization fails, the following CSS classes are set on the HTML body:

  • deferred-bootstrap-loading while the data is loading
  • deferred-bootstrap-error if an error occurs in a resolve function and the app can not be bootstrapped

Have a look at the demo pages to see this in action.

Advanced usage

You can have multiple constants resolved for your app and you can do in the resolve function whatever is necessary before the app is started. The only constraint is, that the function has to return a promise.

To handle exceptions when the promises are resolved, you can add an onError function to the configuration object.

Example:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  resolve: {
    APP_CONFIG: ['$http', function ($http) {
      return $http.get('/api/demo-config');
    }],
    OTHER_CONSTANT: ['$http', '$q', '$timeout', function ($http, $q, $timeout) {
      var deferred = $q.defer();
      $timeout(function () {
        deferred.resolve('MyConstant');
      }, 2000);
      return deferred.promise;
    }]
  },
  onError: function (error) {
	alert('Could not bootstrap, error: ' + error);
  }
});

Attach constants to specific modules

By default, any constants specified in the resolve object will be attached to the module specified in the module option. If you have a need to attach constants to different modules then this can be achieved by using moduleResolves:

window.deferredBootstrapper.bootstrap({
        element: document.body,
        module: 'myApp',
        moduleResolves: [
            {
                module: 'myApp.settings',
                resolve: {
                    CONSTANT_ONE: ['$http', function ($http) {
                        return $http.get();
                    }],
                    CONSTANT_TWO: ['$http', function ($http) {
                        return $http.get();
                    }]
                }
            },
            {
                module: 'myApp.moreSettings',
                resolve: {
                    CONSTANT_THREE: ['$http', function ($http) {
                        return $http.get();
                    }]
                }
            }
        ]
    })

In the above example, CONSTANT_ONE and CONSTANT_TWO will be added to the 'myApp.settings' module and CONSTANT_THREE will be added to the 'myApp.moreSettings' module. There are no limits on how many moduleResolve objects you create and also no limit on the number of constants per moduleResolve

Note that only resolve or moduleResolves can be used - using both in the same configuration will throw an exception

Custom injector modules

By default, the injector that calls your resolve functions only provides the services from the AngularJS core module ng. If you have a use case where you want to use one of your existing services to get configuration at bootstrap time, you can specify which modules should be made available and inject services from those modules in the resolve function. An example is below:

deferredBootstrapper.bootstrap({
        element: document.body,
        module: 'myApp',
        injectorModules: 'myApp.settings',
        resolve: {
            SETTINGS: ['SettingsService', function (SettingsService) {
                return SettingsService.get('/settings');
            }]
        }
    });

The injectorModules option can also take an array of modules. If you have multiple services spread across different modules you can also inject them:

deferredBootstrapper.bootstrap({
        element: document.body,
        module: 'myApp',
        injectorModules: ['myApp.settings', 'myApp.foo']
        resolve: {
            SETTINGS: ['SettingsService', function (SettingsService) {
                return SettingsService.get('/settings');
            }],
            FOO: ['FooService', function (FooService) {
                return FooService.get('/foo');
            }]
        }
    });

Note that the services which are injected in your resolve functions will be instantiated again when the actual app starts. This means you can not save any state in your services in the resolve functions.

Bootstrap Config and StrictDi

To set the AngularJS strictDi mode, or any future angular.boostrap config parameters, pass in an optional config object called bootstrapConfig:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  bootstrapConfig: {
    strictDi: true
  },
  resolve: {
    APP_CONFIG: ['$http', function ($http) {
      return $http.get('/api/demo-config');
    }]
  }
});

Testing

Since the constants that deferredBootstrapper adds to your applications module are not available in your unit tests, it makes sense to provide them in a global beforeEach():

beforeEach(function () {
  module(function ($provide) {
    $provide.constant('APP_CONFIG', { someUrl: '/dummyValue' });
  });
});

License

MIT © Philipp Denzler