4

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>

http://i.imgur.com/GpjemTI.png


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;
};

1 Answer 1

3

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">
Sign up to request clarification or add additional context in comments.

10 Comments

This works well when I console.log($scope.bookings) and get some data, but in the HTML/view nothing appears.
When pushing into newBookingArray, I tried console.log(newBookingArray). I get 5 objects, however they never make it to the view. Something wrong with this: <span ng-repeat="otherBooking in otherBookings">
$scope.otherBookings is always undefined. Like it doesn't get assigned.
Then you have a second problem. You should examine the plunker I post (which shows a sample use case) and check how your code structure is different. Otherwise, posting another question (if possible, include a plunker or jsfiddle).
lol. I just read the 1.2rc parser source & find out it handles promise differently from 1.0.7 (the one used in the plunker). See my updated answer.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.