Skip to content Skip to sidebar Skip to footer

Alternative To Angular.element(document).ready

I've noticed that most questions relating to this subject are with regards to an alternative for the jQuery $(document).ready function in angular, which is angular.element($documen

Solution 1:

Generally Angular application is already bootstrapped on document ready. This is default behaviour for automatic bootstrapping with ng-app, and manual bootstrapping with angular.bootstrap should be performed on ready as well.

The question is specific to current case (Microsoft's Bing Maps API). Considering that ready is suggested by Microsoft, a developer is on his/her own with better alternatives.

<script src="https://www.bing.com/api/maps/mapcontrol"></script>

is loaded synchonously, but it triggers a number of dependencies to load which aren't loaded yet at the moment when initial document ready is triggered. Actually, it requires ready inside another ready in order to complete the initialization, this is exactly what the original code and Microsoft example show, and it doesn't look very good.

In order to avoid race conditions application bootstrap can be postponed to the moment when all prerequisites will be loaded, i.e. window load event instead of document ready. It may provide considerable delay but it guarantees that scripts that the application relies on were loaded, regardless of how their transport is performed:

angular.element(window).on('load', () => {
  angular.bootstrap(document.body, ['app']
});

The alternative that API provides to control initialization process is global callback function:

<script src="https://www.bing.com/api/maps/mapcontrol?callback=globalCallbackName"></script>

A callback can be packed with a service instead of relying on <script>:

angular.module('bingMaps', [])
.factory('bingMapsLoader', ($q, $window, $document, $timeout) => {
  var script = document.createElement('script');
  script.src = 'https://www.bing.com/api/maps/mapcontrol?callback=bingMapsCallback';
  script.async = true;

  $document.find('body').append(script);

  return $q((resolve, reject) => {
    $window.bingMapsCallback = resolve;
    $timeout(reject, 30000);
  });
});

bingMapsLoader promise can be chained to guarantee that API was initialized, put into router resolver, etc.

Additionally, controller constructor is executed before directive is being compiled. Whether third-party APIs are used or not, it is correct to move all DOM-specific code to pre/post link function in Angular 1.4 and lower and to controller $onInit or $postLink hook in Angular 1.5 or higher:

app.controller('FooController', function (bingMapsLoader) {
  this.$postLink = () => {
    bingMapsLoader.then(() => this.mapsInit());
  };

  this.mapsInit = () => {
    Microsoft.Maps.Map(...);
  };
  ...

Post a Comment for "Alternative To Angular.element(document).ready"