Reputation: 8939
I'm having a problem with AngularJS and a function that is called before data array being filled. When my function is called in ng-init
, the $scope.bookings
array hasn't been evaluated (filled with data), resulting in no data.
My goal is to: Get all bookings for a certain bookingType and a certain date, and display all of them in a <td>
Here is my HTML code:
Description: I loop through all bookingTypes
and then through all dates
. The ng-init
is executed as many times as the dates are, and that works. otherBookings
should be an array of bookings for that bookingType and that date. However, $scope.bookings
hasn't been filled with data, so otherBookings
loop is never executed.
<tr ng-repeat="bookingType in bookingTypes">
<td>{{bookingType.Name}}]</td>
<td ng-repeat="date in dates" ng-init="otherBookings = checkOtherBookings(bookingType.ID, date)">
<span ng-repeat="otherBooking in otherBookings">
<a ng-href="/Bookings/Edit/{{otherBooking.Booking.ID}}"><span >{{otherBooking.Customer.FirstName}}</span></a>
</span>
</td>
</tr>
Here is my JavaScript code:
Description: In top of the BookingsController
I call a service which fills the $scope.bookings
array with data and the $scope.checkOtherBookings()
function is below that:
BookingService.getAllBookings().then(function(data) {
$scope.bookings = data.data;
});
$scope.checkOtherBookings = function(bookingType, date) {
console.log($scope.bookings);
var newBookingArray = [];
for(var i = 0; i < $scope.bookings.length; i++) {
if($scope.bookings[i].Booking.Type == bookingType) {
var tmpDateFrom = $scope.bookings[i].Booking.DateFrom;
var tmpDateTo = $scope.bookings[i].Booking.DateTo;
if(date >= tmpDateFrom && date <= tmpDateTo) {
newBookingArray.push($scope.bookings[i]);
}
}
}
return newBookingArray;
};
The $scope.checkOtherBookings()
function is called 22 times (as many times as the dates are), but the console.log($scope.bookings)
is outputting 22 times []
- so my observation is that $scope.bookings
array is empty every time the function is called.
I need some kind of a method to wait to execute the ng-init
until the $scope.bookings
array is filled with data.
Any ideas?
UPDATE 1 - 14.09.13
Now newBookingArray
has some data but otherBookings
in ng-init
never gets them.
HTML:
<tr ng-repeat="bookingType in bookingTypes">
<td>{{bookingType.Name}}</td>
<td ng-repeat="date in dates" ng-init="otherBookings = checkOtherBookings(bookingType.ID, date)">
<span ng-repeat="otherBooking in otherBookings">
<a ng-href="/Bookings/Edit/{{otherBooking.Booking.ID}}"><span >{{otherBooking.Customer.FirstName}}</span></a>
</span>
</td>
</tr>
JavaScript:
BookingService.getAllBookings().then(function(data) {
$scope.bookings = data.data;
});
$scope.checkOtherBookings = function(bookingTypeID, date) {
var deferred = $q.defer();
$scope.$watch('bookings', function(bookings) {
if(!bookings.length) return;
var newBookingArray = [];
for(var i = 0; i < $scope.bookings.length; i++) {
if($scope.bookings[i].Booking.Type == bookingTypeID) {
var tmpDateFrom = $scope.bookings[i].Booking.DateFrom;
var tmpDateTo = $scope.bookings[i].Booking.DateTo;
if(date >= tmpDateFrom && date <= tmpDateTo) {
newBookingArray.push($scope.bookings[i]);
console.log(JSON.stringify(newBookingArray));
}
}
}
deferred.resolve(newBookingArray);
});
return deferred.promise;
};
UPDATE 2 - 14.09.13
This is resolved! I moved the assignment from ng-init
to the ng-repeat
and it works perfectly.
HTML:
<tr ng-repeat="bookingType in bookingTypes">
<td>{{bookingType.Name}}</td>
<td ng-repeat="date in dates">
<span ng-repeat="otherBooking in checkOtherBookings(bookingType.ID, date)">
<a ng-href="/Bookings/Edit/{{otherBooking.Booking.ID}}"><span >{{otherBooking.Customer.FirstName}}</span></a>
</span>
</td>
</tr>
JavaScript:
BookingService.getAllBookings().then(function(data) {
$scope.bookings = data.data;
});
$scope.checkOtherBookings = function(bookingTypeID, date) {
var newBookingArray = [];
for(var i = 0; i < $scope.bookings.length; i++) {
if($scope.bookings[i].Booking.Type == bookingTypeID) {
var tmpDateFrom = $scope.bookings[i].Booking.DateFrom;
var tmpDateTo = $scope.bookings[i].Booking.DateTo;
if(dateInRange(tmpDateFrom, tmpDateTo, date)) {
newBookingArray.push($scope.bookings[i]);
}
}
}
return newBookingArray;
};
Upvotes: 4
Views: 800
Reputation: 50345
You can return a promise inside checkOtherBookings
. AngularJS parser automatically deals with promises. So your code would look like this:
$scope.checkOtherBookings = function(bookingType, date) {
var deferred = $q.defer();
$scope.$watch('bookings', function(bookings) {
if (!bookings) return;
var newBookingArray = [];
for(var i = 0; i < $scope.bookings.length; i++) {
if($scope.bookings[i].Booking.Type == bookingType) {
var tmpDateFrom = $scope.bookings[i].Booking.DateFrom;
var tmpDateTo = $scope.bookings[i].Booking.DateTo;
if(date >= tmpDateFrom && date <= tmpDateTo) {
newBookingArray.push($scope.bookings[i]);
}
}
}
deferred.resolve(newBookingArray);
});
return deferred.promise;
};
I've created a plunker that demonstrates the technique here: demo link.
Updated:
The above approach works for AngularJS 1.0.x. The parser of AngularJS 1.2RC (and possibly 1.1.x) handles function returning promise differently, specifically it doesn't return the promise but immediately returning the internal $$v
of the promise, which is undefined
because it's not yet resolved. If you use 1.2, I suggest get rid of ng-init
and try one of the approaches below.
Approach #1:
$scope.$watch('bookings', function(bookings) {
if (!bookings) return;
// filter bookings, then set $scope.otherBookings = filteredList
});
Approach #2:
$scope.getOtherBookings = function() {
// return filter list here
}
<span ng-repeat="otherBooking in getOtherBookings()">
Approach #3:
<span ng-repeat="otherBooking in bookings | customFilterFunction">
Upvotes: 3