webentwicklung-frage-antwort-db.com.de

Angularjs Ladebildschirm auf Ajax-Anfrage

Mit Angularjs muss ein Ladebildschirm (ein einfacher Spinner) angezeigt werden, bis eine Ajax-Anforderung abgeschlossen ist. Bitte schlagen Sie eine Idee mit einem Code-Snippet vor.

114

Anstatt eine Gültigkeitsbereichsvariable einzurichten, um den Datenladestatus anzuzeigen, ist es besser, wenn eine Anweisung alles für Sie erledigt:

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();
                    }
                });
            }
        };

    }]);

Mit dieser Anweisung müssen Sie nur ein beliebiges Lade-Animationselement mit einem Attribut "Laden" versehen:

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

Sie können auf der Seite mehrere Laderadeln haben. Wo und wie diese Spinner angeordnet werden sollen, liegt bei Ihnen, und die Direktive schaltet sie einfach automatisch ein/aus.

204
David Lin

Hier ist ein Beispiel. Es verwendet die einfache ng-show-Methode mit einem 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>

ANGLEJS

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

Natürlich können Sie den HTML-Code der Ladebox in eine Direktive verschieben und dann $ watch für $ scope.loading verwenden. In welchem ​​Fall:

HTML:

<loading></loading>

ANGULARJS-RICHTLINIE:

  .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

54
jzm

Ich benutze dafür ngProgress .

Fügen Sie "ngProgress" zu Ihren Abhängigkeiten hinzu, sobald Sie die Skript-/css-Dateien in Ihren HTML-Code aufgenommen haben. Wenn Sie dies getan haben, können Sie so etwas einrichten, das ausgelöst wird, wenn eine Routenänderung erkannt wurde.

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

Für AJAX -Anfragen können Sie Folgendes tun:

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

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

Denken Sie daran, vorher 'ngProgress' zu den Controller-Abhängigkeiten hinzuzufügen. Wenn Sie mehrere AJAX - Anforderungen ausführen, verwenden Sie eine inkrementelle Variable im Hauptanwendungsbereich, um zu verfolgen, wann Ihre AJAX - Anforderungen abgeschlossen sind, bevor Sie 'ngProgress.complete ();' aufrufen.

20
Nej Kutcharian

die Verwendung von pendingRequests ist nicht korrekt, da diese Eigenschaft, wie in der Angular-Dokumentation erwähnt, hauptsächlich für Debugging-Zwecke gedacht ist.

Ich empfehle, einen Interceptor zu verwenden, um zu wissen, ob ein aktiver Async-Anruf vorliegt. 

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;
            }
        };
    });
}]);

und dann durch eine $ watch prüfen, ob activeCalls in der Anweisung null ist oder nicht.

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();
                }
            });
        }
    };
});
13
Mahdi K.

Am besten verwenden Sie dazu response interceptors zusammen mit der benutzerdefinierten -Direktive. Der Prozess kann mithilfe des pub/sub - Mechanismus mithilfe der Methoden $ rootScope. $ Broadcast & $ rootScope. $ On weiter verbessert werden.

Da der gesamte Prozess in einem gut geschriebenen Blogartikel dokumentiert ist, werde ich das hier nicht noch einmal wiederholen. In diesem Artikel erfahren Sie, welche Implementierung Sie benötigen.

6

In Bezug auf diese Antwort

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

Für mich ist die beste Lösung, aber es gibt eine Möglichkeit, jQuery zu vermeiden.

.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>

Sie müssen $ (Element) .show () ersetzen. und (Element) .show (); mit $ scope.loadingStatus = 'true'; und $ scope.loadingStatus = 'false';

Dann müssen Sie diese Variable verwenden, um das Attribut ng-show des Elements festzulegen.

3
Karsus

TypScript- und Winkel-Implementierung  

Richtlinie

((): 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;
    }
})();

Umfang

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

Vorlage

<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
Code-EZ

Außerdem gibt es eine Nice-Demo, die zeigt, wie Sie die Angularjs-Animation in Ihrem Projekt verwenden können. 
Der Link ist hier
http://yearofmoo-articles.github.io/angularjs-animation-article/app/#/ng-repeat(Siehe linke obere Ecke).
Es ist eine Open Source. Hier ist der Link zum Download 
https://github.com/yearofmoo-articles/AngularJS-Animation-Article
Und hier ist der Link zum Tutorial;
http://www.yearofmoo.com/2013/04/animation-in-angularjs.html
Mein Standpunkt ist, laden Sie die Quelldateien herunter und sehen Sie, wie sie den Spinner implementiert haben. Sie hätten vielleicht einen etwas besseren Ansatz verwendet. Also checkt dieses Projekt aus.

2
DotNet Dreamer

Fügen Sie dies in Ihre "app.config" ein:

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

Und fügen Sie diesen Code hinzu:

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
BratisLatas

Wenn Sie Restangular verwenden (was großartig ist), können Sie während des API-Aufrufs eine Animation erstellen. Hier ist meine Lösung. Fügen Sie einen Antwort-Interceptor und einen Request-Interceptor hinzu, der einen Rootscope-Broadcast sendet. Erstellen Sie dann eine Direktive, die auf diese Antwort und Anforderung wartet:

         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;
      });
    });
  }]);

Hier ist die Direktive:

        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;
        });
      }
    };
  })
;
2
Enkode

Verwenden Sie angle-busy :

Fügen Sie cgBusy als App/Modul hinzu:

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

Fügen Sie Ihr Versprechen zu scope hinzu:

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

In Ihrer HTML-Vorlage:

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

Hier ein einfaches Interceptor-Beispiel. Ich setze die Maus ein, wenn ajax startet, und setze sie auf auto, wenn ajax endet.

$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;
  }
};
});

Code muss in der Anwendung config app.config hinzugefügt werden. Ich habe gezeigt, wie man die Maus beim Laden ändert, aber darin ist es möglich, den Inhalt des Loader anzuzeigen/auszublenden oder einige CSS-Klassen hinzuzufügen oder zu entfernen, die den Loader anzeigen.

Interceptor wird bei jedem ajax-Aufruf ausgeführt, sodass für jeden http-Aufruf keine speziellen booleschen Variablen ($ scope.loading = true/false usw.) erstellt werden müssen.

Interceptor verwendet eingebautes Winkel jqLitehttps://docs.angularjs.org/api/ng/function/angular.element , so dass keine Jquery erforderlich ist.

1
Maciej Sikora

Erstellen Sie eine Direktive mit den Attributen show und size (Sie können auch weitere hinzufügen) 

    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>'
  }
})

und in HTML verwenden als 

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

In der Variablen show übergeben Sie true, wenn ein Versprechen ausgeführt wird, und geben Sie false ein, wenn die Anforderung abgeschlossen ist . Aktive Demo - Angular Loader-Direktive Beispieldemo in JsFiddle

1
Partha Roy

Sie können eine Bedingung hinzufügen und diese dann über das Rootscope ändern. Vor Ihrer Ajax-Anforderung rufen Sie einfach $ rootScope auf. $ 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();
                        }
                    });
                }
            };

        }]);

Dies ist definitiv nicht die beste Lösung, würde aber trotzdem funktionieren.

0
User_3535

Ich habe die Antwort von @ DavidLin etwas aufgebaut, um sie zu vereinfachen - alle Abhängigkeiten von jQuery in der Direktive zu entfernen. Ich kann bestätigen, dass dies funktioniert, wenn ich es in einer Produktionsanwendung verwende

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;
            });
        }
    };
}   

Ich benutze einen ng-show anstelle eines jQuery-Aufrufs, um den <div> auszublenden/anzuzeigen.

Hier ist der <div>, den ich direkt unter den <body>-Tag der Eröffnung platziert habe:

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

Und hier ist das CSS, das das Overlay zur Verfügung stellt, um die Benutzeroberfläche zu blockieren, während ein Aufruf von $ http ausgeführt wird:

.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;
}

Die CSS-Gutschrift geht an @Steve Seeger - seinen Beitrag: https://stackoverflow.com/a/35470281/335545

0
Bern