使用带有ng-show的Angular承诺

时间:2016-07-15 09:02:08

标签: javascript angularjs promise angular-promise

我有一个Angular SPA,它从后端加载一堆数据并显示它们,比方说表。为了提供更好的用户体验,我在加载数据时隐藏这些表并显示一个微调器。所以我最终编写了这样的代码:

模板:

<div class="row">
  <div class="col-md-12">
    <spinner ng-show="ctrl.loading.unicorns"></spinner>
    <ul ng-hide="ctrl.loading.unicorns">
      <li ng-repeat="unicorn in ctrl.unicorns">{{ unicorn.name }}</li>
    </ul>
  </div>
</div>

控制器:

function unicornController() {
  var ctrl = this;

  ctrl.loading = {
    unicorns: true
  };

  unicornService
    .get()
    .then(function(data) {
      ctrl.unicorns = data;
    })
    .finally(function() {
      ctrl.loading.unicorns = false;
    });

  return ctrl;
}

使用一个装载程序并不是什么大不了的事,但是当我在每个视图中都有3个装载程序时,感觉加载可以以更好的方式处理。我发现promises有一个$$state.status属性,它确实保存了这个值,但是afaik我不应该使用它,因为它不是公共API的一部分。有没有其他方法可以实现这一点而不会弄乱本地标志?

2 个答案:

答案 0 :(得分:1)

您可以找到自己做的方法,也可以找到现有方法。

以下是一个有用的项目,可以自动显示$http个请求的进度。

Angular loading bar

安装(npm或bower)

$ npm install angular-loading-bar
$ bower install angular-loading-bar

包含

angular.module('myApp', ['angular-loading-bar'])

答案 1 :(得分:0)

如果您正在做很多事情并且需要独立加载器,那么为此实现特定的指令/组件是个好主意吗?

使用spinKit微调器的简单示例。

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

angular.module('app').component('loading', {
  transclude: true,
  template: `
    <div class="spinner" ng-show="$ctrl.loading"></div>
    <ng-transclude ng-hide="$ctrl.loading"></ng-transclud>
  `,
  bindings: {
    promise: '<'
  },
  controller: function() {
    this.loading = false;

    this.$onChanges = function() {
      if (this.promise != null) {
        this.loading = true;
        this.promise
          .then(function() {
            this.loading = false;
          }.bind(this));
      }
    }.bind(this);
  }
});

angular.module('app').controller('Example', function($timeout) {
  this.emitPromise = function(propName) {
    this[propName] = createRandomPromise()
      .then(function(result) {
        this[propName + 'Result'] = result;
      }.bind(this));
  }.bind(this);

  function createRandomPromise() {
    var time = Math.round(Math.random() * 3000); // up to 3s

    return $timeout(function() {
      return time;
    }, time);
  }
});
.spinner {
  width: 40px;
  height: 40px;
  background-color: #333;
  margin: 10px auto;
  -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
  animation: sk-rotateplane 1.2s infinite ease-in-out;
}
@-webkit-keyframes sk-rotateplane {
  0% {
    -webkit-transform: perspective(120px)
  }
  50% {
    -webkit-transform: perspective(120px) rotateY(180deg)
  }
  100% {
    -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg)
  }
}
@keyframes sk-rotateplane {
  0% {
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
  }
  50% {
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
  }
  100% {
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js"></script>
<div ng-app="app" ng-controller="Example as Ex">
  <loading promise="Ex.x">
    x result = {{Ex.xResult}}
  </loading>
  <br>
  <button type="button" ng-click="Ex.emitPromise('x')">emit x</button>
  <br>
  <loading promise="Ex.y">
    y result = {{Ex.yResult}}
  </loading>
  <br>
  <button type="button" ng-click="Ex.emitPromise('y')">emit y</button>
</div>

或者,如果您希望可以使用指令将结果插入到转换范围:

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

angular.module('app').directive('loading', function($parse) {
  return {
    restrict: 'E',
    transclude: true,
    scope: true,
    template: `
      <div class="spinner" ng-show="loading"></div>
      <div class="target" ng-hide="loading"></div>
    `,
    link: function($scope, $element, $attrs, $ctrl, $transclude) {
      $scope.loading = false;
      
      var targetElem = angular.element($element[0].querySelector('.target'));

      $scope.$watch(watchFn, function(promise) {
        if (promise != null) {
          $scope.loading = true;
          promise.then(function(result) {
              $scope.loading = false;
              $scope.result = result;
            
              $transclude($scope, function (content) {
                targetElem.empty();
                targetElem.append(content);
              });
            });
        }
      });
      
      function watchFn() {
        return $parse($attrs.promise)($scope);
      }
    }
  };
});

angular.module('app').controller('Example', function($timeout) {
  this.parentValue = 'parent Value';
  
  this.emitPromise = function(propName) {
    this[propName] = createRandomPromise();
  }.bind(this);

  function createRandomPromise() {
    var time = Math.round(Math.random() * 3000); // up to 3s

    return $timeout(function() {
      return time;
    }, time);
  }
});
.spinner {
  width: 40px;
  height: 40px;
  background-color: #333;
  margin: 10px auto;
  -webkit-animation: sk-rotateplane 1.2s infinite ease-in-out;
  animation: sk-rotateplane 1.2s infinite ease-in-out;
}
@-webkit-keyframes sk-rotateplane {
  0% {
    -webkit-transform: perspective(120px)
  }
  50% {
    -webkit-transform: perspective(120px) rotateY(180deg)
  }
  100% {
    -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg)
  }
}
@keyframes sk-rotateplane {
  0% {
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
  }
  50% {
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
  }
  100% {
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js"></script>
<div ng-app="app" ng-controller="Example as Ex">
  <loading promise="Ex.x">
    x result = {{result}}
  </loading>
  <br>
  <button type="button" ng-click="Ex.emitPromise('x')">emit x</button>
  <br>
  <loading promise="Ex.y">
    y result = {{result}}<br>
    Ex.parentValue = {{Ex.parentValue}}
  </loading>
  <br>
  <button type="button" ng-click="Ex.emitPromise('y')">emit y</button>
</div>