Gaui
Gaui

Reputation: 8939

Function called before filling $scope array

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

Upvotes: 4

Views: 800

Answers (1)

Buu
Buu

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

Related Questions