【问题标题】:Show Waiting Gif for Multiple Async Ajax Calls显示多个异步 Ajax 调用的等待 Gif
【发布时间】:2014-09-28 05:02:59
【问题描述】:

我正在使用 AngularJS 和一些 jQuery 插件制作一个页面,其中 angular-js 控制器通过异步调用 ajax 的一些 servlet 方法来初始化其模型。我想在第一个 ajax 调用开始时显示加载 gif,并在最后一个 ajax 调用完成时隐藏它。

由于我不知道最后一个 ajax 调用完成,我不知道在哪里放置隐藏加载 gif 的指令。我怎样才能实现这种行为?

我想要的代码示例:

myApp.controller("adminCtrl", function($scope, $http) {
    $scope.initData1 = function() {
        /* is this the first call? then show the gif */
        $http(...).success(function() { /* is this the last response received? then hide the gif */});
    }

    $scope.initData2 = function() {
        /* is this the first call? then show the gif */
        $http(...).success(function() { /* is this the last response received? then hide the gif */});
    }

    $scope.initData3 = function() {
        /* is this the first call? then show the gif */
        $http(...).success(function() { /* is this the last response received? then hide the gif */});
    }

    initData1();
    initData2();
    initData3();
}

我希望你能理解我的问题并知道任何方法来实现这一点。

【问题讨论】:

  • 我接受@Valentin Waeselynck 的回答,因为它可以在多个控制器中重复使用(这是我的情况),而且我不确定 Chandermani 使用 Promise 的回答是否适用于 IE8+ 等浏览器(这也是我的案子)。尽管如此,我认为 Chandermani 关于 promises 的回答是现代浏览器的最佳方法。加载栏也很酷。

标签: javascript jquery ajax angularjs


【解决方案1】:

查看进度条,例如http://chieffancypants.github.io/angular-loading-bar/,它可以在发出 http 请求时显示进度。它基本上是一个 http 拦截器,用于跟踪 http 请求的数量并相应地显示\隐藏进度条。

如果你想解决这个特定的场景,你可以使用 $q.all 来实现这个行为。首先对于所有 init* 函数返回 http 承诺

 $scope.initData1 = function() {
        /* is this the first call? then show the gif */
        var promise = $http(...);
        promise.success(function(data) { // handle data});
        return promise;
    }

现在在调用代码中就可以了

//show animation
$q.all([$sope.initData1(),$sope.initData2(),$sope.initData3()]).then(function(responseArray) {
   //hide animation
})

【讨论】:

  • 我真的很喜欢那个进度条,但我不知道它会如何处理多个并发的 ajax 帖子。它会等待最后一个完成还是会在每个新的并发ajax调用时重新启动? (无论如何我都会试一试)
  • 另一个问题,既然 $q 是一个 ES6 实现,是否意味着它只支持 ES6-ready 浏览器?我至少可以在 IE8 上运行它吗?我需要额外的插件(Ej. jQuery)来模拟所需的 ES6 功能吗?
【解决方案2】:

如何:声明一个服务,您可以在其中注册待处理的任务,然后声明它们已完成。

myApp.factory('pendingService', [function () {
  var pendingTasksCount = 0;

  return {
    anyPending: function () {
      return pendingTasksCount > 0;
    },
    registerNewTask: function () {
      pendingTasksCount += 1;
      return function declareTaskDone() {
        pendingTasksCount -= 1;
      }
    }
  };
}]);

然后在你的控制器中:

myApp.controller("adminCtrl", function($scope, $http, pendingService) {

  // expose "any pending task" property in scope
  $scope.showGif = pendingService.anyPending;

  $scope.initData1 = function() {
    var declareDone  = pendingService.registerNewTask();
    $http(...).success(function() {
      // do stuff
      // ...
      declareDone();
    });
  };

  $scope.initData2 = function() {
    var declareDone  = pendingService.registerNewTask();
    $http(...).success(function() {
      // do stuff
      // ...
      declareDone();
    });
  };

  $scope.initData3 = function() {
    var declareDone  = pendingService.registerNewTask();
    $http(...).success(function() {
      // do stuff
      // ...
      declareDone();
    });
  };

  initData1();
  initData2();
  initData3();
});

在 HTML 中:

<img src="mygifurl" alt="loading" ng-show="showGif()"/>

如果您需要这种行为是本地的,而不是全局的,您可以使用同一个工厂来创建本地对象。

【讨论】:

  • 我喜欢这个答案,因为它独立于控制器,因此可以在其他控制器中重复使用,这就是我的情况。谢谢
  • 我必须在 declareDone() 或 registerNewTask() 上使用 $scope.$apply() 来更新我的视图吗?
  • 在我的项目中遇到过这种情况,我想说您通常不必这样做,因为触发 registerNewTask()declareDone() 的事件已经成为角循环的一部分(例如:单击按钮,然后是 HTTP 响应)。
【解决方案3】:

您可以使用$.active 检查有多少活动的ajax 请求正在进行中。

myApp.controller("adminCtrl", function($scope, $http) {
    $scope.initData1 = function() {
        if($.active==0){
            //this is the last active ajax request
        }
        /* is this the first call? then show the gif */
        $http(...).success(function() { /* is this the last response received? then hide the gif */});
    }

    $scope.initData2 = function() {
        if($.active==0){
            //this is the last active ajax request
        }
        /* is this the first call? then show the gif */
        $http(...).success(function() { /* is this the last response received? then hide the gif */});
    }

    $scope.initData3 = function() {
        if($.active==0){
            //this is the last active ajax request
        }
        /* is this the first call? then show the gif */
        $http(...).success(function() { /* is this the last response received? then hide the gif */});
    }

    initData1();
    initData2();
    initData3();
}

【讨论】:

  • 如果这行得通,惯用的角度方式宁愿是在恕我直言的范围内公开 $.active 属性。
  • 我更喜欢 angular 相关的解决方案,而不是 jquery 相关的解决方案,因为我只将 jQuery 用于某些插件并在 angularjs 中管理所有控制器逻辑。另外,jQuery 是如何知道 angularjs ajax 帖子的?
【解决方案4】:

我会为此使用 ngClass 和一个计数器变量,例如

<html ng-app="testApp">
    <head>
        <style class="text/css">
            .showing {
                /* instead of this, you can add your gif here */
                background: #f0f;
            }
        </style>
    </head>
    <body ng-controller="TestController">
        <div class="test" ng-class="{'showing': count > 0 && count < 3}">
            Count: {{count}}
        </div>
        <script type="text/javascript" src="angular.min.js"></script>
        <script type="text/javascript">
            (function() {
                var testApp = angular.module("testApp", []);
                testApp.controller("TestController", function($scope) {
                    $scope.count = 0;

                    var timeout = function(t) {
                        // this represents your ajax callbacks
                        setTimeout(function() {
                            $scope.$apply(function() {
                                $scope.count = $scope.count + 1;
                            });
                        }, t * 1000);
                    };

                    timeout(1);
                    timeout(2);
                    timeout(3);
                });
            })();
        </script>
    </body>
</html>

您需要修改 $scope.$apply 函数上下文中的范围(在此特定示例中),否则您的视图将不会更新(有关为什么会发生这种情况的更多信息,请参阅:http://www.jeffryhouser.com/index.cfm/2014/6/2/How-do-I-run-code-when-a-variable-changes-with-AngularJS)。如果你使用promises,你也许可以避免这个问题,但我相信使用ngClass是修改视图的角度方式。

【讨论】:

  • 在这种情况下,我假设我应该保留一个 count $scope 变量,并在每个 ajax 发布/响应中递增/递减它。这是正确的吗?
  • 是的,您应该在每次 ajax 调用返回时递增的范围内有一个“计数”变量(如您在示例中所见)。您可以将这种方法与 Chandermani 建议的方法混合使用,一旦所有承诺完成,您就可以拥有一个布尔标志,ngClass 可以根据需要使用它来添加或删除类。
猜你喜欢
  • 1970-01-01
  • 2013-08-03
  • 2017-06-29
  • 1970-01-01
  • 1970-01-01
  • 2012-05-04
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多