4

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}]}
1
  • 1
    $http is asynchronous...you can't loop over the returned array until it has been populated Commented Feb 21, 2016 at 17:25

3 Answers 3

1

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

8 Comments

there is no $scope in a service... use promise for this
Ah of course - I've changed it to use a callback, but a promise is probably better
Yes is definitely better, need to consider what happens if request fails there is a complete disconnect at controller which has no way to catch that failure.
I've added in a version which returns a promise. Untested though, so if you see any glaring errors, let me know and I'll fix!
Unfortunately that's an anti-pattern. $http itself returns a promise and success is deprecated (see docs) in favor of using then() promise chain
|
1

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

18 Comments

Its antipattern to do data manipulation in service... this service calls becomes obsolute coz you can't use this in other places where you want different data instead of manipulated one here... just my thoughts...
@Thalaivar if so then it's easy to set up service with more methods that can be combined within the service. Most style guides will tell you to keep controllers lean and do processing in service
@charlietfl Sorry for the late reply. Thanks for answering my question, i seem to be getting errors in my service. The holidayEvents array isn't getting populated?
@charlietfl I have just fixed a few formatting errors. There are no errors in the console but the events are not loading into the calendar at all? I have console.logged the holidayEvents in the controller and it all seems fine?
So data is making it to controller? And in format expected?
|
0

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.

3 Comments

Idea is good but still no guarantee that service request will have completed, or possibly failed. The resolve is still returning empty array because that's what service is returning regardless if request succeeds or not ...not a promise
@charlietfl: The controller won't get instantiated if you have a failed promise, the routes get rejected... else your view gets loaded and everything works as expected... resolve returns a promise... correct me if i am wrong...
There is no failed promise is the point. OP doesn't return a promise , only an empty array return holidays; . So that gets returned regardless if the $http succeeds or not. The resolve will never get rejected. Also if the $http takes 2 seconds the empty array will still end up in controller

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.