0

I am trying to implement loading wheel directive when we make ajax call so during the response time i want to display loading wheen, With below code i do not see any error neither the loading wheel.

Is there any better way to implement loading wheel using angularJs ?

or

What is implemented wrong in below code ?

main.html

<loading></loading>

<ul style="list-style: none;">
    <li ng-repeat="message in event | limitTo:1000" ng-class="{lastItem: $last}"><span>{{message.value}}</span></li>
</ul>

loading.js

angular.module('App', [])
  .directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" width="20" height="20" />LOADING...</div>',
        link: function (scope, element, attr) {
              scope.$watch('loading', function (val) {
                  if (val)
                      $(element).show();
                  else
                      $(element).hide();
              });
        }
      }
  })

ctrl.js

$scope.searchServerFile = function() {
        console.log($scope.vm.search);
        if ($scope.vm.search === undefined || $scope.vm.search === null) {
            toastr.warning('A search keyword must only contain a-z, A-Z, 0-9, or space characters.');
        } else {
            $scope.loading = true;
            searchFactory.getServerSearch($scope.vm.search, searchEnv).then(function(response) {
                    $scope.modalInstance.rendered.then(function() {
                        $rootScope.$broadcast('displaySearchResults', {
                            messageObj: response.data

                        });
                        $scope.loading = false;
                    });
                }
        }
1
  • check my answer Commented Mar 9, 2017 at 17:26

4 Answers 4

3

Instead of showing/hiding for each service call explicitly, you can use a directive to track the ajax request and show/hide the loading symbol at one place.

Below is the similar implementation

Place the loading icon container in index.html

<div class="loading-icon-container" loading>
    <div class="loading-icon">
        <img src="https://i.sstatic.net/oQ0tF.gif" width="28px" height="28px" alt="Loading..." />
    </div>
</div>

Styling

.loading-icon-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: black;
  z-index: 99999;
  opacity: 0.6;
}

.loading-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  padding: 5px;
  border: 1px solid black;
}

Implement loading directive

return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        scope.$watch(function() {
            return $http.pendingRequests.length > 0;
        }, function(hasPending) {
            if (hasPending) {
                element[0].style.display = '';
            } else {
                element[0].style.display = 'none';
            }
        });
    }
}

Demo

angular
  .module('myApp', []);

angular
  .module('myApp')
  .controller('MyController', MyController)
  .directive('loading', loading)
  .factory('serviceFactory', serviceFactory);

MyController.$inject = ['$scope', 'serviceFactory'];

function MyController($scope, serviceFactory) {

  $scope.serviceCall = function() {
    var reqObj = {
      url: 'https://reqres.in/api/users?delay=3/photos',
      method: 'GET'
    }
    serviceFactory
      .serviceCall(reqObj)
      .then(function(data) {
        $scope.responseText = data.data;
        console.log(data);
      });
  };
}

loading.$inject = ['$http'];

function loading($http) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      scope.$watch(function() {
        return $http.pendingRequests.length > 0;
      }, function(hasPending) {
        if (hasPending) {
          element[0].style.display = '';
        } else {
          element[0].style.display = 'none';
        }
      });
    }
  };
}

serviceFactory.$inject = ['$http'];

function serviceFactory($http) {
  var obj = {};
  obj.serviceCall = function(reqObj) {
    return $http(reqObj).then(function(success) {
      return success.data;
    });
  };
  return obj;

}
.loading-icon-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: black;
  z-index: 99999;
  opacity: 0.6;
}

.loading-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  padding: 5px;
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>

<div ng-app="myApp" ng-controller="MyController">
  <button ng-click="serviceCall()">Servie call</button>
  <!-- loading icon -->
  <div class="loading-icon-container" loading>
    <div class="loading-icon"><img src="https://i.sstatic.net/oQ0tF.gif" width="28px" height="28px" alt="Loading..." />
    </div>
  </div>
  <!--<div ng-bind="responseText|json">
  </div>-->

</div>

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

7 Comments

In my case i am calling factory in controller, is it possible to use that case with your logic
I just used $http in controller for the demo purpose. Ideally you should use factory to fetch data. Yes. It will work whenever there is an AJAX call pending no matter whether you are calling in controller or using factory
so in case of factory how i will use loading() function ?
I dont see in your code how you are using loading directive, You have mixed up everything in one file like controller,factory and directive. can you give me separate example of directive using with the factory. I am fairly new to directives,
@hussain I can understand you are new to Directives. I've given working example in file because stack snippets doesn't allow to create multiple files.
|
0

In a nutshell, what I would do instead would be to always have the loading wheel in the view, and just use ng-if to control whether it shows or not.

// view
<loading-wheel ng-if="vm.loading"/>
<content ng-if="!vm.loading"/>

And then, whenever you go to load, just set vm.loading (probably this.loading depending on context) to true, and when done, false. Then the loading wheel will show up when it's ready, and go away when it's done.

It's a lot cleaner and easier than manually showing and hiding it.

Comments

0

Implementing an interceptor for HTTP requests. Inside the interceptor you will fire events that you will be able to listen across the application.

Once it is happening you can use the events inside your main controller(or some directive) to show hide the loader.

MyApp.factory('Global-httpInterceptor', ['$q', '$rootScope', '$log', function ($q, $rootScope, $log) {

    var numLoadings = 0;

    return {
        request: function (config) {

            numLoadings++;
            // Show loader
            $rootScope.$broadcast("ajax:start", { $config: config });
            return config || $q.when(config);

        },
        response: function (response) {

            if ((--numLoadings) === 0) {
                // Hide loader
                $rootScope.$broadcast("ajax:finished", { $config: response });
            }
            $rootScope.$broadcast("ajax:success", { $response: response });

            return response || $q.when(response);

        },
        responseError: function (response) {

            if (!(--numLoadings)) {
                // Hide loader
                $rootScope.$broadcast("ajax:finished", { $response: response });
            }
            $rootScope.$broadcast("ajax:error", { $response: response });
            return $q.reject(response);
        }
    };
}])
.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push('Global-httpInterceptor');
}]);

Example Use

angular.module('App', [])
  .directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" width="20" height="20" />LOADING...</div>',
        link: function (scope, element, attr) {
            $rootScope.$on("ajax:start", function () {
                element.show();
            });
            $rootScope.$on("ajax:finished", function () {
                element.hide()
            });
        }
      }
  })

And at the view remains the same

<loading></loading>

14 Comments

can you give me an example how i will use this one from controller and view.
Yes, just added
Do we need to set boolean values in controller when we http request ?
No. Just add the "loader" directive at your loader html code and it will automatically shows/hide
On your case, you could just listen the events inside your "loading" directive, instead of creating the one in my example
|
0

Jeesk. Most of these answers seem like overkill, why not just include the image in your controller view and toggle it with an ng-show or ng-if?

( Please pardon my use of the API or whatever. Obviously the $http request should be abstracted into a service or something. I did this quickly and it's been a bit since I have worked with Angular 1)

angular.module("whatever")
  .controller("thing", function($scope, $http /* and whatever else*/) {


  $scope.isShowing = false;

  $scope.makeAJAXCall = function() {
    $scope.isShowing = true;

    $http.get("whatever.com")
      .success(function() {
      $scope.isShowing = false;
    });

  }
});

And the HTML:

<div ng-controller="thing">
   <img src="http://placehold.it/150" alt="" ng-show="{{isShowing}}"/>
</div> 

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.