Introduction

The module provides your application with loading overlays to indicate async operations. It is designed to reduce code duplication in controllers and views of your angular application.

Inspired by angular-growl-2

Options

The module provides 2 levels of configuration:

  • Global configuration level
  • Directive configuration level

Global configuration

Global configuration is applied to all directives by default. It provides the following options:


        angular.module('app', [
  'bsLoadingOverlay'
]).run(function (bsLoadingOverlayService) {
  bsLoadingOverlayService.setGlobalConfig({
    delay: 0, // Minimal delay to hide loading overlay in ms.
    activeClass: undefined, // Class that is added to the element where bs-loading-overlay is applied when the overlay is active.
    templateUrl: undefined // Template url for overlay element. If not specified - no overlay element is created.
    templateOptions: undefined // Options that are passed to overlay template (specified by templateUrl option above).
  });
});

        

Directive configuration

Directive configuration overrides global configuration and provides the following options:

bs-loading-overlay-reference-id
Reference id for the overlay. When it is used in start, wrap or createHandler options - only this overlay is activated. undefined by default.
bs-loading-overlay-active-class
Class that is added to the current div when overlay is activated. empty by default.
bs-loading-overlay-template-url
Template url for the element created inside this div when the overlay is activated.
bs-loading-overlay-template-options
Options that are passed to overlay template rendering scope. Currently used to for integration with Spin.js. See angular-loading-overlay-spinjs.

Examples

If reference id is not passed to start function - then all loaders which don't have specified reference id are triggered.

Some content here

        app.controller('GlobalController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.start();
    };

    $scope.hideOverlay = function() {
        bsLoadingOverlayService.stop();
    }
});

        
<div ng-app="app-global">
  <div ng-controller="GlobalController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay()">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-global', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

If reference id is passed to start function - then only overlays with specified ids are triggered.

Some content here
Some content here

        app.controller('WithReferenceController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function(referenceId) {
        bsLoadingOverlayService.start({
            referenceId: referenceId
        });
    };

    $scope.hideOverlay = function(referenceId) {
        bsLoadingOverlayService.stop({
            referenceId: referenceId
        });
    }
});

        
<div ng-app="app-with-reference">
  <div ng-controller="WithReferenceController">
    <button class="btn btn-primary" ng-click="showOverlay('first')">Show overlay first</button>
    <button class="btn btn-primary" ng-click="hideOverlay('first')">Hide overlay first</button>
    <button class="btn btn-primary" ng-click="showOverlay('second')">Show overlay second</button>
    <button class="btn btn-primary" ng-click="hideOverlay('second')">Hide overlay second</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-reference-id="first"
>
      Some content here
    </div>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-reference-id="second"
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-with-reference', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

Or it can be passed with directive name attribute

Some content here
Some content here

        app.controller('WithReferenceInNameAttributeController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function(referenceId) {
        bsLoadingOverlayService.start({
            referenceId: referenceId
        });
    };

    $scope.hideOverlay = function(referenceId) {
        bsLoadingOverlayService.stop({
            referenceId: referenceId
        });
    }
});

        
<div ng-app="app-with-reference-in-name-attribute">
  <div ng-controller="WithReferenceInNameAttributeController">
    <button class="btn btn-primary" ng-click="showOverlay('first-in-name')">Show overlay first</button>
    <button class="btn btn-primary" ng-click="hideOverlay('first-in-name')">Hide overlay first</button>
    <button class="btn btn-primary" ng-click="showOverlay('second-in-name')">Show overlay second</button>
    <button class="btn btn-primary" ng-click="hideOverlay('second-in-name')">Hide overlay second</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay="first-in-name"
>
      Some content here
    </div>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay="second-in-name"
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-with-reference-in-name-attribute', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

If templateUrl is not specified in global config and in start option and in directive attributes - then no overlay element is created.

On the other hand you can pass active class to directive or start function. In this case the class is added directly to the element where overlay directive is applied.

Some content here

        app.controller('ClassController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.start();
    };

    $scope.hideOverlay = function() {
        bsLoadingOverlayService.stop();
    }
});

        
<div ng-app="app-class">
  <div ng-controller="ClassController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay()">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-active-class="loading-overlay--active"
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-class', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]);

        

To reduce flickering of loading overlay on possibly fast async actions you can add minimal delay to hide overlay.

Some content here

        app.controller('DelayController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.start();
    };

    $scope.hideOverlay = function() {
        bsLoadingOverlayService.stop();
    }
});

        
<div ng-app="app-delay">
  <div ng-controller="DelayController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay()">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-delay="3000"
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-delay', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

To reduce duplication you can create preconfigured handler for your overlay. Then the overlay can be started with simple function call, without options.

Some content here

        app.controller('HandlerController', function($scope, bsLoadingOverlayService) {
    var overlayHandler = bsLoadingOverlayService.createHandler({
        referenceId: 'handler-overlay'
    });

    $scope.showOverlay = function() {
        overlayHandler.start();
    };

    $scope.hideOverlay = function() {
        overlayHandler.stop();
    }
});

        
<div ng-app="app-handler">
  <div ng-controller="HandlerController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay()">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-reference-id="handler-overlay"
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-handler', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

You can wrap any promise with `wrap` function and then the overlay start function will be called right after the wrap call and stop function will be called on promese resolved or rejected. A promise can also be wrapped with handler (`handler.wrap(somePromiseHere)`).

Some content here

        app.controller('WrapperController', function($scope, $timeout, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.wrap({}, $timeout(angular.noop, 5000));
    }
});

        
<div ng-app="app-wrapper">
  <div ng-controller="WrapperController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-wrapper', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

The same as above, except that you also can wrap a function that returns a promise.

Some content here

        app.controller('WrapperFunctionController', function($scope, $timeout, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.wrap({},
            function() {
                return $timeout(angular.noop, 5000);
            });
    }
});

        
<div ng-app="app-wrapper-function">
  <div ng-controller="WrapperFunctionController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-wrapper-function', [
    'bsLoadingOverlay',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

Examples of integration with Spin.js Spin.js

To use default Spin.js integration with default options:

  • Install angular-loading-overlay-spinjs package.
    npm install -S angular-loading-overlay-spinjs
  • Add node_modules/angular-loading-overlay-spinjs/dist/angular-loading-overlay-spinjs.js script to your page
  • Add bsLoadingOverlaySpinJs dependency to your app module
  • Set global option templateUrl to bsLoadingOverlaySpinJs in your app module run block

Some content here

        app.controller('GlobalSpinjsController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.start();
    };

    $scope.hideOverlay = function() {
        bsLoadingOverlayService.stop();
    }
});

        
<div ng-app="app-global-spinjs">
  <div ng-controller="GlobalSpinjsController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay()">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-global-spinjs', [
    'bsLoadingOverlay',
    'bsLoadingOverlaySpinJs',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'bsLoadingOverlaySpinJs'
    });
});

        

You can customize the spinner with options from Spin.js site, providing options to bs-loading-overlay-template-options attribute.

Some content here

        app.controller('SpinjsOptionsController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.start();
    };

    $scope.hideOverlay = function() {
        bsLoadingOverlayService.stop();
    }
});

        
<div ng-app="app-spinjs-options">
  <div ng-controller="SpinjsOptionsController">
    <button class="btn btn-primary" ng-click="showOverlay('with-options')">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay('with-options')">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-template-options="{radius:8, width:2, length: 5, color: 'red'}"
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-spinjs-options', [
    'bsLoadingOverlay',
    'bsLoadingOverlaySpinJs',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'bsLoadingOverlaySpinJs'
    });
});

        

Also you can specify Spin.js options globally in your app module run block.

Some content here

        app.controller('GlobalSpinjsOptionsController', function($scope, bsLoadingOverlayService) {
    $scope.showOverlay = function() {
        bsLoadingOverlayService.start();
    };

    $scope.hideOverlay = function() {
        bsLoadingOverlayService.stop();
    }
});

        
<div ng-app="app-global-spinjs-options">
  <div ng-controller="GlobalSpinjsOptionsController">
    <button class="btn btn-primary" ng-click="showOverlay()">Show overlay</button>
    <button class="btn btn-primary" ng-click="hideOverlay()">Hide overlay</button>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
>
      Some content here
    </div>
  </div>
</div>

        var app = angular.module('app-global-spinjs-options', [
    'bsLoadingOverlay',
    'bsLoadingOverlaySpinJs',
    'ui.bootstrap'
]).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'bsLoadingOverlaySpinJs'
    });

    bsLoadingOverlayService.setGlobalConfig({
        templateOptions: {
            radius: 8,
            width: 2,
            length: 4,
            lines: 5,
            color: 'purple'
        }
    });
});

        

Examples of integration with $http service

To use default Spin.js integration with default options:

  • Install angular-loading-overlay-http-interceptor package.
    npm install -S angular-loading-overlay-http-interceptor
  • Add node_modules/angular-loading-overlay-http-interceptor/dist/angular-loading-overlay-http-interceptor.js script to your page
  • Add bsLoadingOverlayHttpInterceptor dependency to your app module
  • Add interceptor like in the code below.

Fetch random text:


        app.controller('HttpIntegrationController', function($scope, $http, $sce, bsLoadingOverlayService) {
    $scope.result = $sce.trustAsHtml('Fetch result here');
    $scope.fetchRandomText = function() {
        $http.get('http://hipsterjesus.com/api/')
            .success(function(data) {
                $scope.result = $sce.trustAsHtml(data.text);
            })
            .error(function() {
                $scope.result = $sce.trustAsHtml('Can not get the article');
            });
    };
});

        
<div ng-app="app-http-integration">
  <div ng-controller="HttpIntegrationController">
    <style>
      .fetch-random-result {
              overflow: auto;
              height: 300px;
              margin-top: 1em;
          }
    </style>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-delay="3000"
>
      Fetch random text: <button ng-click="fetchRandomText()">Fetch</button>
      <div class="fetch-random-result">
        <p ng-bind-html="result"></p>
      </div>
    </div>
  </div>
</div>

        var app = angular.module('app-http-integration', [
    'bsLoadingOverlay',
    'bsLoadingOverlayHttpInterceptor',
    'ui.bootstrap'
])
.factory('allHttpInterceptor', function(bsLoadingOverlayHttpInterceptorFactoryFactory) {
    return bsLoadingOverlayHttpInterceptorFactoryFactory();
})
.config(function($httpProvider) {
    $httpProvider.interceptors.push('allHttpInterceptor');
}).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

You can provide referenceId option to interceptor factory.

Fetch random text:


        app.controller('HttpIntegrationWithReferenceIdController', function($scope, $http, $sce, bsLoadingOverlayService) {
    $scope.result = $sce.trustAsHtml('Fetch result here');
    $scope.fetchRandomText = function() {
        $http.get('http://hipsterjesus.com/api/')
            .success(function(data) {
                $scope.result = $sce.trustAsHtml(data.text);
            })
            .error(function() {
                $scope.result = $sce.trustAsHtml('Can not get the article');
            });
    };
});

        
<div ng-app="app-http-integration-with-reference-id">
  <div ng-controller="HttpIntegrationWithReferenceIdController">
    <style>
      .fetch-random-result {
              overflow: auto;
              height: 300px;
              margin-top: 1em;
          }
    </style>
    <div
    class="well well-lg bs-loading-container"
    bs-loading-overlay
    bs-loading-overlay-reference-id="all-ajax-spinner"
    bs-loading-overlay-delay="3000"
>
      Fetch random text: <button ng-click="fetchRandomText()">Fetch</button>
      <div class="fetch-random-result">
        <p ng-bind-html="result"></p>
      </div>
    </div>
  </div>
</div>

        var app = angular.module('app-http-integration-with-reference-id', [
    'bsLoadingOverlay',
    'bsLoadingOverlayHttpInterceptor',
    'ui.bootstrap'
])
.factory('allHttpInterceptor', function(bsLoadingOverlayHttpInterceptorFactoryFactory) {
    return bsLoadingOverlayHttpInterceptorFactoryFactory({
        referenceId: 'all-ajax-spinner'
    });
})
.config(function($httpProvider) {
    $httpProvider.interceptors.push('allHttpInterceptor');
}).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

It is also possible to provide requests matcher to interceptors which allows to show spinners for particular requests.

{{randomUser.name.first}} {{randomUser.name.last}}


        app.controller('HttpIntegrationWithReferenceIdAndMatchersController', function($scope, $http, $sce, bsLoadingOverlayService) {
    $scope.randomText = $sce.trustAsHtml('Fetch result here');
    $scope.randomUser = undefined;

    $scope.fetchRandomText = function() {
        $http.get('http://hipsterjesus.com/api/')
            .success(function(data) {
                $scope.randomText = $sce.trustAsHtml(data.text);
            })
            .error(function() {
                $scope.randomText = $sce.trustAsHtml('Can not get the article');
            });
    };

    $scope.fetchRandomUser = function() {
        $http.get('https://randomuser.me/api/')
            .success(function(data) {
                $scope.randomUser = data.results[0];
            });
    };
});

        
<div ng-app="app-http-integration-with-reference-id-and-matchers">
  <div ng-controller="HttpIntegrationWithReferenceIdAndMatchersController">
    <style>
      .random-result {
              display: flex;
              height: 300px;
              margin-top: 1em;
          }
      
          .random-result__text, .random-result__user {
              position: relative;
              overflow: auto;
              border: 2px dashed #C00;
              flex: 1;
              margin: 1em;
              padding: 1em;
              text-align: center;
          }
      
          .user__photo {
              width: 150px;
              height: 150px;
              border-radius: 50%;
              margin: 20px;
          }
      
          .user__name {
              font-size: 2em;
              text-align: center;
          }
    </style>
    <div
    class="well well-lg bs-loading-container"
>
      <button ng-click="fetchRandomText()">Fetch random text</button>
      <button ng-click="fetchRandomUser()">Fetch random user</button>
      <div class="random-result">
        <div
            class="random-result__text"
            bs-loading-overlay
            bs-loading-overlay-reference-id="random-text-spinner"
            bs-loading-overlay-delay="3000"
        >
          <p ng-bind-html="randomText"></p>
        </div>
        <div
            class="random-result__user user"
            bs-loading-overlay
            bs-loading-overlay-reference-id="random-user-spinner"
            bs-loading-overlay-delay="3000"
        >
          <div ng-if="randomUser">
            <img ng-src="{{randomUser.picture.large}}" alt="" class="user__photo" />
            <p class="user__name">
              {{randomUser.name.first}} {{randomUser.name.last}}
                </p>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

        var app = angular.module('app-http-integration-with-reference-id-and-matchers', [
    'bsLoadingOverlay',
    'bsLoadingOverlayHttpInterceptor',
    'ui.bootstrap'
])
.factory('randomTextInterceptor', function(bsLoadingOverlayHttpInterceptorFactoryFactory) {
    return bsLoadingOverlayHttpInterceptorFactoryFactory({
        referenceId: 'random-text-spinner',
        requestsMatcher: function (requestConfig) {
            return requestConfig.url.indexOf('hipsterjesus') !== -1;
        }
    });
})
.factory('randomUserInterceptor', function(bsLoadingOverlayHttpInterceptorFactoryFactory) {
    return bsLoadingOverlayHttpInterceptorFactoryFactory({
        referenceId: 'random-user-spinner',
        requestsMatcher: function (requestConfig) {
            return requestConfig.url.indexOf('randomuser') !== -1;
        }
    });
})
.config(function($httpProvider) {
    $httpProvider.interceptors.push('randomTextInterceptor');
    $httpProvider.interceptors.push('randomUserInterceptor');
}).run(function(bsLoadingOverlayService) {
    bsLoadingOverlayService.setGlobalConfig({
        templateUrl: 'loading-overlay-template.html'
    });
});

        

Changelog

  • 1.1.0 - Template options and intergration with Spin.js.
  • 1.0.0 - Transition to typescript. Wrap arguments change.
  • 0.3.1 - Fix for version in package.json.
  • 0.3.0 - Fix for version according to semver. Fixes for package.json.
  • 0.2.1 - Ability to pass reference id with directory name attribute.
  • 0.2.0 - Glowl-like API and documentation.