能否在延迟后使用ng-show指令?

9

我有一个旋转器,它是通过ng-show="loading>0"显示的。

有没有办法延迟显示这个旋转器(比如1秒)?

我不能使用定时器,因为多个请求会导致加载计数器失去同步。

我需要在ng-show上添加延迟,可以通过CSS转换或类似方法实现。


你已经查看文档了吗?链接 向下滚动,您将看到一个带有过渡和ng-show的示例。 - graphefruit
你真正想要在这里实现什么?你想要有一个延迟,以便对于快速操作它不会只是闪烁,但对于较长时间的操作它会显示出来吗?只是一个通用的加载指示器? - Josh
3个回答

7
我的猜测是你正在寻找一个包含延迟的通用旋转器。标准是在200ms后显示或类似的时间。 这是指令的一个完美候选者,实际上非常容易实现。
我知道这是一个长的代码示例,但主要部分是该指令。它相当简单。
监听一些作用域变量并在可配置的延迟后显示。如果操作时间超过延迟时间,它将被取消而从未显示出来。

(function() {
  'use strict';

  function SpinnerDirective($timeout) {
    return {
      restrict: 'E',
      template: '<i class="fa fa-cog fa-spin"></i>',
      scope: {
        show: '=',
        delay: '@'
      },
      link: function(scope, elem, attrs) {
        var showTimer;

        //This is where all the magic happens!
        // Whenever the scope variable updates we simply
        // show if it evaluates to 'true' and hide if 'false'
        scope.$watch('show', function(newVal){
          newVal ? showSpinner() : hideSpinner();
        });
        
        function showSpinner() {
          //If showing is already in progress just wait
          if (showTimer) return;

          //Set up a timeout based on our configured delay to show
          // the element (our spinner)
          showTimer = $timeout(showElement.bind(this, true), getDelay());
        }

        function hideSpinner() {
          //This is important. If the timer is in progress
          // we need to cancel it to ensure everything stays
          // in sync.
          if (showTimer) {
            $timeout.cancel(showTimer);
          }

          showTimer = null;

          showElement(false);
        }

        function showElement(show) {
          show ? elem.css({display:''}) : elem.css({display:'none'});
        }

        function getDelay() {
          var delay = parseInt(scope.delay);

          return angular.isNumber(delay) ? delay : 200;
        }
      }
    };
  }

  function FakeService($timeout) {
    var svc = this,
      numCalls = 0;

    svc.fakeCall = function(delay) {
      numCalls += 1;

      return $timeout(function() {

        return {
          callNumber: numCalls
        };

      }, delay || 50);
    };
  }

  function MainCtrl(fakeService) {
    var vm = this;

    vm.makeCall = function(delay) {
      vm.isBusy = true;
      fakeService.fakeCall(delay)
        .then(function(result) {
          vm.result = result;
        }).finally(function() {
          vm.isBusy = false;
        });
    }
  }

  angular.module('spinner', [])
    .service('fakeService', FakeService)
    .controller('mainCtrl', MainCtrl)
    .directive('spinner', SpinnerDirective);

}());
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>

<div class="container" ng-app="spinner">
  <div class="row" ng-controller="mainCtrl as ctrl">
    <div class="col-sm-12">
      <h2>{{ctrl.result | json}}
        <spinner show="ctrl.isBusy" delay="200"></spinner>
      </h2>
      <button type="button" 
              class="btn btn-primary" 
              ng-click="ctrl.makeCall(2000)" 
              ng-disabled="ctrl.isBusy">Slow Call
      </button>
      <button type="button" 
              class="btn btn-default" 
              ng-click="ctrl.makeCall()" 
              ng-disabled="ctrl.isBusy">Fast Call
      </button>
    </div>
  </div>
</div>


1
优秀的解决方案! - jdnichollsc

5
这里有一个更简单的方法符合我的需求。根据你的操作不同,你可以将函数setDelay()绑定到元素上。例如,在我的情况下,我将setDelay()绑定到了一个选择输入框上。
触发HTML:
<select class="first-option"
    ng-change="setDelay()" 
    ng-options="o.label for o in download.options" 
    ng-model="optionModel" required>
</select>

在您的控制器中,添加一个简单的函数setDelay,它将更改标志$scope.delay
$scope.setDelay = function(){
    $scope.delay = true;
    $timeout(function(){
        $scope.delay = false;
    }, 200);
};

那么,您可以直接在ng-show中使用$scope.delay作为标志:

<div class="loading-div" ng-show="delay">
    <img src="loading_spinner.gif">
</div>

并在加载完成后显示内容:

<div ng-show="!delay">
    Content is loaded.
</div>

现在,每当用户在下拉菜单中选择新值时,它会触发$scope.delay被设置为true,从而导致旋转图标显示。当$scope.delay达到200时,它将被设置为false,从而导致旋转图标隐藏。


4
我认为一个纯CSS解决方案是实现它的最佳方法。
这里是一个演示如何实现它的plunker。使用ng-animate类来进行过渡,并应用10ms的过渡延迟(0s的过渡与CSS不兼容)。
代码的相关部分:
.your-element-class.ng-hide {
  opacity: 0;
}

.your-element-class.ng-hide-add,
.your-element-class.ng-hide-remove {
  transition: all linear 0.01s 1s;
}

如果你需要在代码中多次使用不同的延迟值,那么使用自定义指令的唯一原因就是这个。自定义指令可以更灵活地控制延迟时间。


这对我很有帮助,也是一种非常干净的方式,可以避免任何解决方法渗入到js代码中。我喜欢尽可能多地将样式/渲染逻辑保留在CSS中。 - jkchu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接