Екран завантаження Angularjs за запитом ajax


117

Використовуючи Angularjs, мені потрібно показати екран завантаження (простий спінер), поки запит ajax не буде завершений. Запропонуйте будь-яку ідею з фрагментом коду.




Найкращою практикою є використання, $httpProvider.interceptorsоскільки вона може впоратися, коли починається запит ajax і коли він закінчується. Ось чому $watchбільше не потрібно виявляти, коли починається і закінчується виклик Ajax.
Френк М'ят Чт

як щодо того, щоб ви замовили мій заводський кутовий-httpshooter , він дозволяє краще контролювати навантажувачі та заморожувати інтерфейс користувача
Siddharth

Відповіді:


210

Замість того, щоб встановлювати змінну області для вказівки стану завантаження даних, краще, щоб директива робила все для вас:

angular.module('directive.loading', [])

    .directive('loading',   ['$http' ,function ($http)
    {
        return {
            restrict: 'A',
            link: function (scope, elm, attrs)
            {
                scope.isLoading = function () {
                    return $http.pendingRequests.length > 0;
                };

                scope.$watch(scope.isLoading, function (v)
                {
                    if(v){
                        elm.show();
                    }else{
                        elm.hide();
                    }
                });
            }
        };

    }]);

З цією директивою все, що вам потрібно зробити, - це надати будь-якому елементу анімації завантаження атрибут 'loading':

<div class="loading-spiner-holder" data-loading ><div class="loading-spiner"><img src="..." /></div></div>

Ви можете мати кілька завантажувальних вертушок на сторінці. куди і як розташувати ці спінери залежить від вас, і директива просто включить / вимкне їх автоматично.


тут директива "завантаження" викликається двічі з $ location.path (). Спочатку з поточною сторінкою, а потім із цільовою. Що має бути причиною?
Санджай Д

Відмінно. А також ви можете використовувати перемикання jquery на watch: elm.toggle (v). У мене є монітор сеансу $ timeout, і мені потрібно показувати спінер лише через пів секунди. Я реалізую css з переходом, щоб повільніше показувати спінер.
Жоао Поло

7
Якщо ви отримуєте помилку "elm.show () - це не функція"), перед додаванням кутового слід додати jquery.
morpheus05

Те, як ви працюєте. Я повинен був зробити це так, як це робить jzm (використовуючи "" навколо імені та опустивши префікс області). Будь-які ідеї чому?
KingOfHypocrites

3
Що робити, якщо мені потрібно асинхронізувати. завантажувати кілька різних областей?
Вадим Фердерер

56

Ось приклад. Він використовує простий метод ng-show з bool.

HTML

<div ng-show="loading" class="loading"><img src="...">LOADING...</div>
<div ng-repeat="car in cars">
  <li>{{car.name}}</li>
</div>
<button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>

АНГУЛЯРИ

  $scope.clickMe = function() {
    $scope.loading = true;
    $http.get('test.json')
      .success(function(data) {
        $scope.cars = data[0].cars;
        $scope.loading = false;
    });
  }

Звичайно, ви можете перемістити html-код завантажувальної коробки в директиву, а потім використовувати $ watch на $ range.loading. У якому випадку:

HTML :

<loading></loading>

ДИРЕКТИВА АНГУЛЯРЖА :

  .directive('loading', function () {
      return {
        restrict: 'E',
        replace:true,
        template: '<div class="loading"><img src="..."/>LOADING...</div>',
        link: function (scope, element, attr) {
              scope.$watch('loading', function (val) {
                  if (val)
                      $(element).show();
                  else
                      $(element).hide();
              });
        }
      }
  })

PLUNK : http://plnkr.co/edit/AI1z21?p=preview


Те, як ви працює.
KingOfHypocrites

@jzm Таке велике рішення 😊, до жаль , це рішення не працює з ui-router, не знаю , чому
штат Міссурі

Я зробив це в одному з моїх проектів, працював чудово, в іншому проекті, думаю, через важкий запит ajax loading=trueне встановлюють
alamnaryab

21

Для цього я використовую ngProgress .

Додайте 'ngProgress' до своїх залежностей, як тільки ви включите файли script / css у свій HTML. Після цього ви можете налаштувати щось подібне, яке запуститься, коли буде виявлена ​​зміна маршруту.

angular.module('app').run(function($rootScope, ngProgress) {
  $rootScope.$on('$routeChangeStart', function(ev,data) {
    ngProgress.start();
  });
  $rootScope.$on('$routeChangeSuccess', function(ev,data) {
    ngProgress.complete();
  });
});

Для запитів AJAX ви можете зробити щось подібне:

$scope.getLatest = function () {
    ngProgress.start();

    $http.get('/latest-goodies')
         .success(function(data,status) {
             $scope.latest = data;
             ngProgress.complete();
         })
         .error(function(data,status) {
             ngProgress.complete();
         });
};

Просто пам’ятайте, щоб додати «ngProgress» до залежностей контролерів перед цим. І якщо ви робите кілька запитів AJAX, використовуйте додаткову змінну в основній області застосування, щоб відслідковувати, коли ваші запити AJAX закінчені, перш ніж викликати 'ngProgress.complete ();'.


15

використання pendingRequests не є правильним, оскільки, як зазначено в документації Angular, ця властивість в першу чергу призначена для використання в цілях налагодження.

Я рекомендую використовувати перехоплювач, щоб знати, чи є активний виклик Async.

module.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push(function ($q, $rootScope) {
        if ($rootScope.activeCalls == undefined) {
            $rootScope.activeCalls = 0;
        }

        return {
            request: function (config) {
                $rootScope.activeCalls += 1;
                return config;
            },
            requestError: function (rejection) {
                $rootScope.activeCalls -= 1;
                return rejection;
            },
            response: function (response) {
                $rootScope.activeCalls -= 1;
                return response;
            },
            responseError: function (rejection) {
                $rootScope.activeCalls -= 1;
                return rejection;
            }
        };
    });
}]);

а потім перевірте, чи активний виклик дорівнює нулю чи ні в директиві через $ watch.

module.directive('loadingSpinner', function ($http) {
    return {
        restrict: 'A',
        replace: true,
        template: '<div class="loader unixloader" data-initialize="loader" data-delay="500"></div>',
        link: function (scope, element, attrs) {

            scope.$watch('activeCalls', function (newVal, oldVal) {
                if (newVal == 0) {
                    $(element).hide();
                }
                else {
                    $(element).show();
                }
            });
        }
    };
});

7

Найкращий спосіб зробити це - використовувати перехоплювачі відповідей разом із спеціальною директивою . І процес можна вдосконалити за допомогою механізму pub / sub, використовуючи $ rootScope. $ Broadcast & $ rootScope. $ За методами.

Оскільки весь процес задокументований у добре написаній статті блогу , я не збираюсь повторювати це знову. Будь ласка, зверніться до цієї статті, щоб запропонувати необхідну реалізацію.


4

Посилаючись на цю відповідь

https://stackoverflow.com/a/17144634/4146239

Для мене найкраще рішення, але є спосіб уникнути використання jQuery.

.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)
                      scope.loadingStatus = 'true';
                  else
                      scope.loadingStatus = 'false';
              });
        }
      }
  })

  .controller('myController', function($scope, $http) {
      $scope.cars = [];
      
      $scope.clickMe = function() {
        scope.loadingStatus = 'true'
        $http.get('test.json')
          .success(function(data) {
            $scope.cars = data[0].cars;
            $scope.loadingStatus = 'false';
        });
      }
      
  });
<body ng-app="myApp" ng-controller="myController" ng-init="loadingStatus='true'">
        <loading ng-show="loadingStatus" ></loading>
  
        <div ng-repeat="car in cars">
          <li>{{car.name}}</li>
        </div>
        <button ng-click="clickMe()" class="btn btn-primary">CLICK ME</button>
  
</body>

Вам потрібно замінити $ (елемент) .show (); і (елемент) .how (); з $ range.loadingStatus = 'true'; і $ range.loadingStatus = 'false';

Тоді вам потрібно використовувати цю змінну, щоб встановити атрибут ng-show елемента.


4

Машинопис та кутова реалізація

директива

((): void=> {
    "use strict";
    angular.module("app").directive("busyindicator", busyIndicator);
    function busyIndicator($http:ng.IHttpService): ng.IDirective {
        var directive = <ng.IDirective>{
            restrict: "A",
            link(scope: Scope.IBusyIndicatorScope) {
                scope.anyRequestInProgress = () => ($http.pendingRequests.length > 0);
                scope.$watch(scope.anyRequestInProgress, x => {            
                    if (x) {
                        scope.canShow = true;
                    } else {
                        scope.canShow = false;
                    }
                });
            }
        };
        return directive;
    }
})();

Область застосування

   module App.Scope {
        export interface IBusyIndicatorScope extends angular.IScope {
            anyRequestInProgress: any;
            canShow: boolean;
        }
    }  

Шаблон

<div id="activityspinner" ng-show="canShow" class="show" data-busyindicator>
</div>

CSS
#activityspinner
{
    display : none;
}
#activityspinner.show {
    display : block;
    position : fixed;
    z-index: 100;
    background-image : url('data:image/gif;base64,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') 
    -ms-opacity : 0.4;
    opacity : 0.4;
    background-repeat : no-repeat;
    background-position : center;
    left : 0;
    bottom : 0;
    right : 0;
    top : 0;
}

3

Якщо ви користуєтесь передустановою (що дивовижно), ви можете створити анімацію під час дзвінків api. Ось моє рішення. Додайте перехоплювач відповіді та перехоплювач запиту, який надсилає широкомовні кореневища. Потім створіть директиву для прослуховування відповіді та запиту:

         angular.module('mean.system')
  .factory('myRestangular',['Restangular','$rootScope', function(Restangular,$rootScope) {
    return Restangular.withConfig(function(RestangularConfigurer) {
      RestangularConfigurer.setBaseUrl('http://localhost:3000/api');
      RestangularConfigurer.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
        var extractedData;
        // .. to look for getList operations
        if (operation === 'getList') {
          // .. and handle the data and meta data
          extractedData = data.data;
          extractedData.meta = data.meta;
        } else {
          extractedData = data.data;
        }
        $rootScope.$broadcast('apiResponse');
        return extractedData;
      });
      RestangularConfigurer.setRequestInterceptor(function (elem, operation) {
        if (operation === 'remove') {
          return null;
        }
        return (elem && angular.isObject(elem.data)) ? elem : {data: elem};
      });
      RestangularConfigurer.setRestangularFields({
        id: '_id'
      });
      RestangularConfigurer.addRequestInterceptor(function(element, operation, what, url) {
        $rootScope.$broadcast('apiRequest');
        return element;
      });
    });
  }]);

Ось директива:

        angular.module('mean.system')
  .directive('smartLoadingIndicator', function($rootScope) {
    return {
      restrict: 'AE',
      template: '<div ng-show="isAPICalling"><p><i class="fa fa-gear fa-4x fa-spin"></i>&nbsp;Loading</p></div>',
      replace: true,
      link: function(scope, elem, attrs) {
        scope.isAPICalling = false;

        $rootScope.$on('apiRequest', function() {
          scope.isAPICalling = true;
        });
        $rootScope.$on('apiResponse', function() {
          scope.isAPICalling = false;
        });
      }
    };
  })
;

Будь-яка відповідь API вбиває анімацію. Ви повинні зберігати додаткову інформацію про запит - відповідь та реагувати лише на ту відповідь, яка була ініціалізована запитом.
Кшиштоф Саф'яновський

3

Включіть це до своєї програми "app.config":

 $httpProvider.interceptors.push('myHttpInterceptor');

І додайте цей код:

app.factory('myHttpInterceptor', function ($q, $window,$rootScope) {
    $rootScope.ActiveAjaxConectionsWithouthNotifications = 0;
    var checker = function(parameters,status){
            //YOU CAN USE parameters.url TO IGNORE SOME URL
            if(status == "request"){
                $rootScope.ActiveAjaxConectionsWithouthNotifications+=1;
                $('#loading_view').show();
            }
            if(status == "response"){
                $rootScope.ActiveAjaxConectionsWithouthNotifications-=1;

            }
            if($rootScope.ActiveAjaxConectionsWithouthNotifications<=0){
                $rootScope.ActiveAjaxConectionsWithouthNotifications=0;
                $('#loading_view').hide();

            }


    };
return {
    'request': function(config) {
        checker(config,"request");
        return config;
    },
   'requestError': function(rejection) {
       checker(rejection.config,"request");
      return $q.reject(rejection);
    },
    'response': function(response) {
         checker(response.config,"response");
      return response;
    },
   'responseError': function(rejection) {
        checker(rejection.config,"response");
      return $q.reject(rejection);
    }
  };
});

2

Використовуйте кутові зайнятості :

Додайте cgBusy до своєї програми / модуля:

angular.module('your_app', ['cgBusy']);

Додайте обіцянку scope:

function MyCtrl($http, User) {
  //using $http
  this.isBusy = $http.get('...');
  //if you have a User class based on $resource
  this.isBusy = User.$save();
}

У вашому HTML-шаблоні:

<div cg-busy="$ctrl.isBusy"></div>

2

Також є хороша демонстрація, яка показує, як можна використовувати Angularjsанімацію у своєму проекті.

Посилання тут (Дивіться верхній лівий кут) .

Це відкритий код. Ось посилання для завантаження

І ось посилання на підручник ;

Моя суть, продовжуйте завантажувати вихідні файли, а потім подивіться, як вони реалізували спінер. Вони, можливо, використали трохи кращий підхід. Отже, замовіть цей проект.


1

Ось простий приклад перехоплювача, я вмикаю мишу в очікуванні, коли починається ajax, і встановлюю його автоматично, коли ajax закінчується.

$httpProvider.interceptors.push(function($document) {
return {
 'request': function(config) {
     // here ajax start
     // here we can for example add some class or show somethin
     $document.find("body").css("cursor","wait");

     return config;
  },

  'response': function(response) {
     // here ajax ends
     //here we should remove classes added on request start

     $document.find("body").css("cursor","auto");

     return response;
  }
};
});

Код потрібно додати в конфігурацію програми app.config. Я показав, як змінити мишу в стані завантаження, але там можна показати / приховати будь-який вміст завантажувача або додати, видалити деякі класи css, які показують завантажувач.

Перехоплювач виконуватиме кожен виклик ajax, тому не потрібно створювати спеціальні булеві змінні ($ range.loading = true / false тощо) на кожному http-дзвінку.

Interceptor використовує вбудований у кутовий jqLite https://docs.angularjs.org/api/ng/function/angular.element, тому Jquery не потрібен.


1

Створіть директиву за допомогою атрибутів show та size (ви також можете додати більше)

    app.directive('loader',function(){
    return {
    restrict:'EA',
    scope:{
        show : '@',
      size : '@'
    },
    template : '<div class="loader-container"><div class="loader" ng-if="show" ng-class="size"></div></div>'
  }
})

а в html використовувати як

 <loader show="{{loader1}}" size="sm"></loader>

У змінній show передають true, коли виконується будь-яка обіцянка, і роблять це false, коли запит виконується. Активний демонстраційний приклад - приклад демонстраційної директиви Angular Loader в JsFiddle


0

Я побудував на @ DavidLin відповідь трохи спростити її - усунення будь-якої залежності від jQuery в директиві. Я можу підтвердити це, коли я його використовую у виробничому додатку

function AjaxLoadingOverlay($http) {

    return {
        restrict: 'A',
        link: function ($scope, $element, $attributes) {

            $scope.loadingOverlay = false;

            $scope.isLoading = function () {
                return $http.pendingRequests.length > 0;
            };

            $scope.$watch($scope.isLoading, function (isLoading) {
                $scope.loadingOverlay = isLoading;
            });
        }
    };
}   

Я використовую ng-showзамість jQuery виклик, щоб приховати / показати <div>.

Ось <div>що я розмістив трохи під початковим <body>тегом:

<div ajax-loading-overlay class="loading-overlay" ng-show="loadingOverlay">
    <img src="Resources/Images/LoadingAnimation.gif" />
</div>

А ось CSS, який надає накладення для блокування інтерфейсу користувача під час дзвінка $ http:

.loading-overlay {
    position: fixed;
    z-index: 999;
    height: 2em;
    width: 2em;
    overflow: show;
    margin: auto;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
}

.loading-overlay:before {
    content: '';
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.3);
}

/* :not(:required) hides these rules from IE9 and below */
.loading-overlay:not(:required) {
    font: 0/0 a;
    color: transparent;
    text-shadow: none;
    background-color: transparent;
    border: 0;
}

Кредит CSS переходить до @Steve Seeger's - його допис: https://stackoverflow.com/a/35470281/335545


0

Ви можете додати умову, а потім змінити її за допомогою кореневого екрана. Перш ніж ваш запит ajax, ви просто зателефонуєте $ rootScope. $ Emit ('stopLoader');

angular.module('directive.loading', [])
        .directive('loading',   ['$http', '$rootScope',function ($http, $rootScope)
        {
            return {
                restrict: 'A',
                link: function (scope, elm, attrs)
                {
                    scope.isNoLoadingForced = false;
                    scope.isLoading = function () {
                        return $http.pendingRequests.length > 0 && scope.isNoLoadingForced;
                    };

                    $rootScope.$on('stopLoader', function(){
                        scope.isNoLoadingForced = true;
                    })

                    scope.$watch(scope.isLoading, function (v)
                    {
                        if(v){
                            elm.show();
                        }else{
                            elm.hide();
                        }
                    });
                }
            };

        }]);

Це однозначно не найкраще рішення, але воно все одно працює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.