Reputation: 6143
There is an app that allows users create or join rooms. So there is RoomsListController
which displays rooms, RoomDetailsController
which shows contents and controls for activity inside a particular room. And of cause, a RoomService
which provides getRooms()
(async) method.
Now what would be the Angular way to make the two controllers wait for the same data returned by RoomService.getRooms()
?
Details:
RoomService.getRooms()
at application start is not an optionRoomDetailsController
doesn't always request list of rooms. Whenever a room provided via $stateParams
exists and is active it is simply displayed. If not, there is some logic that guides user to another room (here list of rooms is required) or displays a message that no rooms are available and perhaps it's time to create one.getRooms()
is a heavy operation for server and it's not desired to get it twice when same data on client can be reused.Upvotes: 3
Views: 82
Reputation: 38121
Another approach might be to modify the RoomService.getRooms()
function in your service to declare and store a local promise the first time the function is called, while you are waiting for results from the server. If the function gets called again, you would just return the same promise immediately. Once the results are returned from the server, you can resolve the stored promise and clear it, so the next time it gets called, a new request will be made.
Of course, there is plenty of room to customise from here. If you know the data from the server will never change, for instance, you don't need to clear the promise at all. Just return it every time the rooms get asked for.
Here is a sample implementation:
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>
Upvotes: 1
Reputation: 19772
Take a look at route resolve promises.
You'll call the async method before the route resolves:
// 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
}
This way, as far as the controllers are concerned, the data is already there before the route even loads.
You've touched on one of the key issues is applications... that is, creating a single source of truth for data that is composed in a way that the application can consume it.
There are many patterns for solving this, but I'd recommend taking a look at the route resolution promises as a starting point, and branching out from there. It can become very complex.
Upvotes: 2