PI.
PI.

Reputation: 1668

Angular $resource - Data not being returned

I am quite beginner level with both JS and Angular, and I am trying to return data from an API and store it in $scope.

Once I've stored it, I want to loop over each item and output it into the page, pretty basic stuff.

Problem I am having is the API and data is there, but it seems to be returning after the loop is running, is there any way of making the loop wait?

Heres the code;

Service (Hit the endpoint and retrieve the data)

  'use strict';

  function RecruiterDashJobs($resource, API_URL) {
      var dashJobs = {};

      dashJobs.getJobs = function(uuid) {
          return $resource(API_URL + 'recruiters/' + uuid + '/jobs').get();
      }

      return dashJobs;

   }

   angular
       .module('app')
       .service('RecruiterDashJobs', RecruiterDashJobs);

Controller (Call the service and store the data)

   $scope.currentRecruiter = User.getUser();

   $scope.getJobs = function(uuid) {
      var data = RecruiterDashJobs.getJobs(uuid);
      data.$promise.then(
          function(res) {
              return res.jobs
          },
          function(err) {
              return err;
          }
      )
   };

   $scope.recruiterJobs = $scope.getJobs($scope.currentRecruiter.uuid);

View (the Ng-repeat)

   <div class="panel border-bottom pad-s-2x pad-e-1x" ng-repeat="job in recruiterJobs">

       <div class="panel__body">

           <aside class="valign">
               <a class="icon--edit color--echo mar-r-2x" ui-sref="jobs/edit/{{job.uuid}"></a>
           </aside>
           <div class="valign">
               <p>{{job.title}}</p>
               <p class="color--charlie">Closing Date: {{job.closing_date}}</p>
           </div>

       </div>

  </div>

Upvotes: 0

Views: 86

Answers (2)

Matt Greer
Matt Greer

Reputation: 62027

EDIT: the "magical" approach below no longer works as of Angular 1.2

In your getJobs method, the return statements are inside child functions. You aren't returning the data from getJobs, you're returning it from the function you passed to then.

Two ways to fix it:

The magical Angular way for Angular less than 1.2

Angular views will work with promises for you, so you can just change your getJobs method to this:

$scope.getJobs = function(uuid) {
  var data = RecruiterDashJobs.getJobs(uuid);
  return data.$promise.then(
      function(res) {
          return res.jobs
      },
      function(err) {
          return err;
      }
  )
};

Added return data.$promise...

If you want this to still work in Angular 1.2, you need to call $parseProvider.unwrapPromises(true) somewhere in your code, typically on your main modules config block.

Less magical way or for Angular 1.2 and above

If you want to better understand what is going on, then you can do it this way

$scope.getJobs = function(uuid) {
  var data = RecruiterDashJobs.getJobs(uuid);
  data.$promise.then(
      function(res) {
          $scope.recruiterJobs = res.jobs
      },
      function(err) {
          console.log(err);
      }
  )
};

Upvotes: 0

Sycomor
Sycomor

Reputation: 1272

A $resource call is asynchronous, but $resource immediately returns an empty object that you can embed in your page and will be later populated by response contents. If all goes well, angular should spot the change (because it comes from a $resource process that angular monitors) and update your view accordingly. So, the behaviour you observe is normal : the very premise of a $promise is that it will be done at a later stage and the process should proceed anyway. Solutions :

Simply try :

$scope.getJobs = function(uuid) {
   var data = RecruiterDashJobs.getJobs(uuid);
   return data;
};

If you don't need to post-process data, this should be all you need (except that you might need to call recruiterJobs.jobs in your view, if your response does indeed return an object containing a jobs array, and not the array itself). The page will display, with an initial empty div, then update when data are retrieved and ng-repeat discovers new data to add to the page. If you do need some post-processing, you can still use your callback :

$scope.getJobs = function(uuid) {
    var data = RecruiterDashJobs.getJobs(uuid);
    data.$promise.then(
        function(res) {
            //do something
        },
        function(err) {
            return err;
        }
    );
    return data;
};

If you really need to wait for your data (e.g. because there are some downstream processes that you need them for that can't be postponed), you can use the promise to do so :

$scope.getJobs = function(uuid) {
    $scope.preparing = true;
    var data = RecruiterDashJobs.getJobs(uuid);
    data.$promise.then(function(res) {
        $scope.preparing = false;
        return data;      
    });
   };

This way, the function will not return until the promise is resolved. I added an optional $scope.preparing flag that you can use in your page to inform the user that something is loading.

Upvotes: 0

Related Questions