Ben Clarke
Ben Clarke

Reputation: 1061

AngularJS - ForEach not looping

Issue


I have an AngularJS application and for some reason one of my forEach loops are not working. The loop seems to work when the a function on a button click but on the initial load it isn't working.

Code


So I have my controller correctly set up for the page. I am calling a function in my service which fires off to my API and returns me an array:

Controller

var holidaysnew = getUserEventsById.getUserEventsById();

Service

app.service('getUserEventsById', function ($http) {  

this.getUserEventsById = function () {
    var holidays = [];


    $http.get("http://localhost:9847/AceTracker.svc/GetAllEventsByUser?user_id=1").success(function (data) {
        for (var key in data.GetAllEventsByUserResult) {
            if (data.GetAllEventsByUserResult.hasOwnProperty(key)) {
                holidays.push(data.GetAllEventsByUserResult[key])
            }
        }
    });
    return holidays;
};
});

So on the initial page load holidaysnew gets successfully hit and is an array of the holidays. Below the line is where my forEach loop is, I want to loop through holidaysnew but it doesn't seem to go into the loop.

The loop is coded as below:

holidaysnew.forEach(function (hol) {
    $scope.eventSources[0].events.push({
        end: $filter('dateFilter')(hol.HOLIDAY_END),
        start: $filter('dateFilter')(hol.HOLIDAY_START),
        title: hol.HOLIDAY_TITLE,
        color: $filter('colorEventFilter')(hol.HOLIDAY_EVENT_STATE_ID)
    });
});

I have console.log the holidaysnew array and it definitely getting populated. Can anyone see the issue?

EDIT:

The service seems to be fine and return back the array as expected (See below)

enter image description here

But when the $scope.eventSources[0].events = holidayEvents; code gets run it doesn't actually seem to set the events.

Is holidayEvents an array?

EDIT2:

Here is a sample of the JSON returned to the service:

{"GetAllEventsByUserResult":[{"HOLIDAY_END":"\/Date(1456358400000+0000)\/","HOLIDAY_EVENT_ID":1,"HOLIDAY_EVENT_STATE_ID":1,"HOLIDAY_START":"\/Date(1455926400000+0000)\/","HOLIDAY_TITLE":"Spain     ","USER_ID":1},{"HOLIDAY_END":"\/Date(1454371200000+0000)\/","HOLIDAY_EVENT_ID":2,"HOLIDAY_EVENT_STATE_ID":2,"HOLIDAY_START":"\/Date(1454284800000+0000)\/","HOLIDAY_TITLE":"Italy     ","USER_ID":1},{"HOLIDAY_END":"\/Date(1458000000000+0000)\/","HOLIDAY_EVENT_ID":3,"HOLIDAY_EVENT_STATE_ID":1,"HOLIDAY_START":"\/Date(1457568000000+0000)\/","HOLIDAY_TITLE":"Germany   ","USER_ID":1},{"HOLIDAY_END":"\/Date(1481068800000+0000)\/","HOLIDAY_EVENT_ID":4,"HOLIDAY_EVENT_STATE_ID":3,"HOLIDAY_START":"\/Date(1480896000000+0000)\/","HOLIDAY_TITLE":"England   ","USER_ID":1}]}

Upvotes: 4

Views: 1519

Answers (3)

charlietfl
charlietfl

Reputation: 171679

Change service to return the $http promise but to also to do all the data manipulation you need in the service itself to create the calendar events and return those.

success is deprecated in favor of using then() promise chain callbacks

A more conventional approach would look like:

app.service('getUserEventsById', function($http, $q) {

  this.getUserEventsById = function() {

    var requestPromise = $http.get("url")
      .then(function(response) {
        var holidays = [];
        var results = response.data.GetAllEventsByUserResult
        for (var key in results) {
          if (results.hasOwnProperty(key)) {
            holidays.push(results[key])
          }
        }
        // return holidays array to next `then()`
        return holidays;
      }).then(function(holidays) {
        var holidayEvents = holidays.map(function(hol) {
          // make sure to inject `$filter` in service
          return {
            end: $filter('dateFilter')(hol.HOLIDAY_END),
            start: $filter('dateFilter')(hol.HOLIDAY_START),
            title: hol.HOLIDAY_TITLE,
            color: $filter('colorEventFilter')(hol.HOLIDAY_EVENT_STATE_ID)
          }

        });
        // return the events array to next `then()`
        return holidayEvents;

      });
    // return the `$http` promise to controller
    return requestPromise;

  };
});

Then in controller all you would need is

getUserEventsById.getUserEventsById().then(function(holidayEvents){
    $scope.eventSources[0].events = holidayEvents;
}).catch(function(){

   // do something if promise chain has a rejection
})

Upvotes: 1

millerbr
millerbr

Reputation: 2961

This looks like an error with handling your REST response. The population of the array is happening after the return statement, as the REST call is asynchronous. Try moving the loop that isn't working into it's own function, and then calling that function inside the REST success callback, or return a Promise and resolve it in the success callback.

Returning a promise:

app.service('getUserEventsById', function ($http, $q) {  

    this.getUserEventsById = function () {
        var deferred = $q.defer();
        var holidays = [];


        $http.get("http://localhost:9847/AceTracker.svc/GetAllEventsByUser?user_id=1").then(function (data) {
            for (var key in data.GetAllEventsByUserResult) {
                if (data.GetAllEventsByUserResult.hasOwnProperty(key)) {
                    holidays.push(data.GetAllEventsByUserResult[key])
                }
            }
            deferred.resolve(holidays);
        });
        return deferred.promise;
    };
});

Looping on the promise:

holidaysnew.then(function(holidays) {
    holidays.forEach(function (hol) {
        $scope.eventSources[0].events.push({
            end: $filter('dateFilter')(hol.HOLIDAY_END),
            start: $filter('dateFilter')(hol.HOLIDAY_START),
            title: hol.HOLIDAY_TITLE,
            color: $filter('colorEventFilter')(hol.HOLIDAY_EVENT_STATE_ID)
        });
    });
});

Upvotes: 1

Thalaivar
Thalaivar

Reputation: 23622

A better solution/approach would be to make your api call from resolve.

angular
    .module('app')
    .config(config);

function config($routeProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'yourTemp.html',
            controller: 'Main',
            controllerAs: 'vm',
            resolve: {
                getUserEventsById: function(getUserEventsById){
                    return getUserEventsById.getUserEventsById()
                }
            }
        });
}

angular
    .module('app')
    .controller('Main', Main);

Main.$inject = ['getUserEventsById'];
function Main(getUserEventsById) {
      var vm = this;
      vm.yourdata = getUserEventsById.data;
}

Now you could run your forEach loop on vm.yourdata, which would run fine.

Upvotes: 0

Related Questions