两个控制器依赖于异步返回的相同数据



有一个允许用户创建或加入房间的应用程序。RoomsListController显示房间,RoomDetailsController显示特定房间内的内容和活动控制。当然,RoomService提供了getRooms()(异步)方法。

现在,Angular如何让两个控制器等待RoomService.getRooms()返回的相同数据呢?

细节:

  • 在应用程序中有认证,房间列表依赖于userId。所以在应用程序启动时调用RoomService.getRooms()不是一个选项
  • RoomDetailsController并不总是要求房间列表。只要通过$stateParams提供的房间存在并且处于活动状态,它就会被简单地显示出来。如果没有,则会有一些逻辑引导用户到另一个房间(这里需要房间列表),或者显示一条消息,提示没有房间可用,也许是时候创建一个房间了。
  • 让我们假设getRooms()对服务器来说是一个繁重的操作,当客户端可以重用相同的数据时,不希望获得两次。
  • 希望具有不依赖于哪个控制器函数首先执行的逻辑,因为这可能受到模板
  • 中的布局更新的影响。

看看路由解析承诺。

你将在路由解析之前调用async方法:
// in your config
$routeProvider.when('/your-route', {
  resolve: {
    roomData: function(RoomService) {
      return RoomService.getRooms();
    }
  }
}
// RoomsListController
function(roomData) {
  // do something with roomData
}
// RoomDetailsController
function(roomData) {
  // do something with roomData
}

这样,对于控制器来说,数据甚至在路由加载之前就已经存在了。

你已经触及到一个关键问题是应用程序…也就是说,为以应用程序可以使用的方式组合的数据创建一个单一的真实源。

有很多模式可以解决这个问题,但我建议从路由解析承诺开始,然后从那里扩展。它可以变得非常复杂。

另一种方法可能是修改服务中的RoomService.getRooms()函数,以便在第一次调用该函数时声明并存储一个本地承诺,同时等待来自服务器的结果。如果函数再次被调用,您只需立即返回相同的承诺。一旦结果从服务器返回,您就可以解析存储的承诺并清除它,以便下次调用它时,将发出新的请求。

当然,这里还有很多定制的空间。例如,如果您知道来自服务器的数据永远不会更改,则根本不需要清除承诺。每次有人要房间的时候就把它还给我。

下面是一个示例实现:

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, RoomService) {
  $scope.name = 'World';
  $scope.fetchingStatus = function() {
    return RoomService.serverRequests ? 'Fetching... Server Requests: ' + RoomService.serverRequests : '';
  };
  $scope.getRooms = function() {
    console.log('asking for rooms')
    RoomService.getRooms().then(function(rooms) {
      console.log('rooms returned: ', rooms);
      $scope.rooms = rooms;
    });
  };
});
function RoomService($q, $timeout) {
  this.$q = $q;
  this.$timeout = $timeout;
  this.getRoomsPromise = null;
  this.serverRequests = 0;
}
RoomService.prototype.getRooms = function() {
  if (!this.getRoomsPromise) {
    this.getRoomsPromise = this.$q.defer();
    this.getRoomsFromServer();
  }
  return this.getRoomsPromise.promise;
};
RoomService.prototype.getRoomsFromServer = function() {
  var rooms = [];
  for (var i = 1; i <= 10; i++) {
    rooms.push({
      id: i,
      name: 'Room ' + i
    });
  }
  console.log('fetching data from server...');
  this.serverRequests++;
  this.$timeout(function() {
    this.serverRequests--;
    this.getRoomsPromise.resolve(rooms);
    this.getRoomsPromise = null;
    console.log('got data from server...')
  }.bind(this), 1500);
}
app.service('RoomService', RoomService);
.fetching {
  background-color: red;
  color: white;
  padding: 2px 10px;
  display: inline-block;
  margin-left: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<div ng-app="plunker" ng-controller="MainCtrl">
  <p>
    <button ng-click="getRooms()">Get Rooms</button>
    <span class="fetching" ng-show="fetchingStatus()">{{fetchingStatus()}}</span>
  </p>
  <ul>
    <li ng-repeat="room in rooms track by room.id">{{room.name}}</li>
  </ul>
</div>

最新更新