0

I created a simple div that contains overlay with "loading" gif.

I want to be able to show/hide this "loader" and have it accessible through any controller.

I believe I need to use a service for that. this is my simple code which fail to work:

MyApp.factory('loader', function() {
    $(".loader").fadeIn();
 });

and then in controllers.js:

MyApp.controller('MyController', function($scope, $rootScope, $auth, $state) {

    $rootScope.loader;

});

But this does nothing. I'm new to angular and I'd appreciate a direction to solve this.

I essentially want to create something like this:

loader.show();
loader.hide();

Of course if anyone think this should be done differently it would be great to hear a more efficient way.

5 Answers 5

3

Something like this ? When app is loaded it will show loader and after 1 sec loader will be hidden.

var app = angular.module('app', []);
app.run(function($rootScope) {
      $rootScope.showLoader = true;
 })

app.controller('Ctrl', function($rootScope, $timeout) {

  $timeout(function(){
   $rootScope.showLoader = false;
  },1000);
 

});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

 
<body ng-app="app" ng-controller="Ctrl">
  <img ng-if='showLoader' src='http://www.arabianbusiness.com/skins/ab.main/gfx/loading_spinner.gif'>
  
</body>

Sign up to request clarification or add additional context in comments.

3 Comments

wow thanks for this. can you check my answer? i'd be happy to know which way is more efficient.
I have been using the above coded loader in many professional apps and I find it right way for loader becuaes its usually full-page so better not create service/factory for it, your style of answer will best fit if loader is just showing on some corner or not blocking screen or its toaster like thing.
Why does it matter if the UI is full screen or not? A factory can display it however he wants without adding to angulars global scope potentially causing conflicts and scope issues. $rootScope should very rarely be used.
1

Uh i actually made it alone, im proud. here's the code for whoever come across this.

MyApp.factory('loader', function() {

    this.show = function(){
        // show loader code..
    }

    this.hide = function(){
        // hide loader code..
    }

    return this;

 });

usage:

loader.show/hide();

1 Comment

This is much better than the other answers to use $rootScope which just pollute the global scope, however there are some best practices you should include, see my answer for an example of how I would do this.
1

To work with the DOM element you must use the directive. They are created for this.

Live example on jsfiddle.

angular.module('ExampleApp', [])
  .controller('ExampleOneController', function($scope, $timeout) {
    $scope.loading = false;
    $scope.loading2 = false;
    $scope.time1 = 1;
    $scope.time2 = 4;
    $scope.emulateRequest1 = function(time) {
      $scope.loading1 = true;
      console.log($scope.loading1,time);
      $timeout(function() {
        $scope.loading1 = false;
        console.log($scope.loading1,time);
      }, time * 1000)
    };
    $scope.emulateRequest2 = function(time) {
      $scope.loading2 = true;
      console.log($scope.loading2,time);
      $timeout(function() {
        $scope.loading2 = false;
        console.log($scope.loading2,time);
      }, time * 1000)
    };
  })
  .directive('loading', function() {
    return {
      restrict: 'E',
      scope: {
        process: "="
      },
      template: '<div ng-show="process">Loading...</div>',
    }
  });;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
  <div ng-controller="ExampleOneController">
    <h3>
      ExampleOneController
    </h3>
    <form name="ExampleForm" id="ExampleForm">
      <input ng-model="time1" placeholder="time in seconds">
      <button ng-click="emulateRequest1(time1)">
        Send emulate request
      </button>
      <br>
      <input ng-model="time2" placeholder="time in seconds">
      <button ng-click="emulateRequest2(time2)">
        Send emulate request 2
      </button>
      <loading process="loading1"></loading>
      <loading process="loading2"></loading>
    </form>
  </div>
</div>

UPDATED

Version with the factory. This solution is wrong.

If you send a first request that is performed for 4 seconds, then that runs for 1 second, you can see that with the loading div gone to request complete with 4 seconds. Look at console.

Live example on jsfiddle

angular.module('ExampleApp', [])
  .controller('ExampleOneController', function($scope, $timeout,LoadingService) {
    $scope.loadingService = LoadingService;
    $scope.loadingService.loading = false;
    $scope.time1 = 1;
    $scope.time2 = 4;
    $scope.emulateRequest = function(time) {
      $scope.loadingService.loading = true;
      console.log($scope.loadingService.loading,time);
      $timeout(function() {
        $scope.loadingService.loading = false;
        console.log($scope.loadingService.loading,time);
      }, time * 1000)
    };
  })
  .service('LoadingService', function() {
    return {
      loading: false,
    }
  });;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
  <div ng-controller="ExampleOneController">
    <h3>
      ExampleOneController
    </h3>
    <form name="ExampleForm" id="ExampleForm">
      <input ng-model="time1" placeholder="time in seconds">
      <button ng-click="emulateRequest(time1)">
        Send emulate request
      </button>
      <br>
      <input ng-model="time2" placeholder="time in seconds">
       <button ng-click="emulateRequest(time2)">
        Send emulate request 2
      </button>
      <div ng-show="loadingService.loading">
      loading...
      </div>
     <div ng-show="loadingService.loading">
      loading...
     </div>
    </form>
  </div>
</div>

2 Comments

Working with a directive makes sense considering a template is involved here, however it's not necessary. The OP can create a factory, add a showLoader boolean on the factory and then use ng-if='showLoader' to show/hide the template.
There are several ways to solve a problem. But there are ways to solve that are preferred over others. For the manipulation of the DOM is better to use an element of the directive, which encapsulate the logic. In your proposal use the factory there is a problem. If you want to use two boot loader on the same page? In this case your solution is not clean.
0

You have to use a $rootScope variable in run function and set it default to true as

angular.module('MyApp', [])
 .run(function($rootScope) {
      $rootScope.showLoader = true;
 })

So whenever a page load it automatically show, now to hide it use

$rootScope.showLoader = false;

in your controller after you got the response from server or somewhere else

 MyApp.controller('MyController', function($scope, $rootScope, $auth, $state) {

 $rootScope.showLoader = false;

});

And for loader, there is no need to use a service.

4 Comments

I posted an answer, mind to have a look? I was wondering, which way is better? FYI. you need to keep in mind that my loader is hidden by default through CSS.
You really shouldn't use $rootScope.
@efarley, suggest a better way & why.
See my answer below, you shouldn't use $rootScope because you're poluting the global scope which should be avoided, a better solution is to use a factory and inject that factory into a module/controller which is used for the entire application.
0

I wouldn't use $rootScope for this as it is using angulars global scope which should be avoided in almost all instances. You should use a factory to do this, you can then inject the factory into you parent module/controller and it will be available to your entire app. I suggest you modulize you code and include application wide methods such as this in a core module.

Here is a working example of how I would do this. http://codepen.io/anon/pen/adQVjX

(function () {
  'use strict';

  angular
    .module('myApp', [])
    .controller('MyCtrl', Ctrl) // Using a named function makes the code more readable and allows other devs to quickly see all of the dependencies in a quick glance at the top of the module
    .factory('loader', loader); // Let's create a loader factory which can contain any properties or methods associated with the loader.

  Ctrl.$inject = ['loader']; // Inject the factory into the controller.

  function Ctrl (loader) { 
    var ctrl = this; // This makes the rest of the code more readable and makes it clear what you are referencing in nested scopes.
    ctrl.showLoader = loader.showLoader; // Set up a method on the controller that calls the associated method on the loader factory. This is really only necessary if you want to call this method from the controllers scope.
    ctrl.hideLoader = loader.hideLoader; // ditto

    return ctrl;
  }

  function loader () {
    var loader = { // Create a loader object which has two methods defined.
      hideLoader: hideLoader,
      showLoader: showLoader
    };

    return loader; // Return our object to the controller.

    function hideLoader () {
      // Hide loader code goes here...
    }

    function showLoader () {
      // Show loader code goes here...
    }
  }
})();

You might use it something like this from a view, although you'll probably just call these methods from inside the controller.

<html ng-app='myApp'>
    <body>
        <div>
          <div ng-controller='MyCtrl as ctrl'>
            <button ng-click='ctrl.showLoader()'>
              Show Loader
            </button>
            <button ng-click='ctrl.hideLoader()'>
              Hide Loader
            </button>
          </div>
        </div>
    </body>
</html>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.