1

I'm having trouble understanding promises. I have a function $scope.startJob that when called, it calls areThereAvailableSegments in the SegmentsService. The idea is that areThereAvailableSegments should return true or false, so that I can handle this behavior in the controller, but not sure how to do this.

areThereAvailableSegments calls getSegments(), which requests text segments from the database. getSegments returns a promise, and I'm trying to handle this promise with .then in areThereAvailableSegments, but not sure how to pass to the controller.

Here's my Segments factory, that returns the $resource object, segmentsfactory.js:

angular.module('appApp')
  .factory('SegmentsFactory', function ($resource) {
    return $resource('http://localhost:3000/api/v1/segments/:id');
  });

Here's my Segments service, segmentsservice.js:

angular.module('appApp')
  .service('SegmentsService', function (SegmentsFactory) {

    // gets segments from mongo
    this.getSegments = function(jobId) {
      return SegmentsFactory.query({ job_id: jobId }).$promise;
    };

    // should return true/false if there are available segments
    this.areThereAvailableSegments = function(jobId) {
      var dump = [];

      // queries for all segments
      this.getSegments(jobId)
      .then(function(segments) {
        // dumps segments status in a temp array
        for (var i = 0; i < segments.length; i++) {
          dump.push(segments[i].status);
        }
        // returns true if there is an 'available' in the temp array
        if (dump.indexOf('available') >= 0) {
          return true;
        } else {
          return false;
        }
      });
    };

  });

And here's my controller, main.js:

$scope.startJob = function() {

  if (SegmentsService.areThereAvailableSegments(jobId)) {
    console.log('there are available segments, then trigger texteditor');
  } else {
    console.log('no segments to translate');
  }
};

When executing, console shows "no segments to translate", because SegmentsService is not returning true or false.

How to solve this?

1
  • This function cannot return true or false, as it doesn't know if the call was successfull or not. Function's is executed way before the promise has chance to be resolved. In short, promises are asynchroous. Commented Aug 21, 2015 at 14:52

3 Answers 3

2

the function areThereAvailibleSegments should return a promise as well. And then in your controller you have a then case and have your if-else there.

this.areThereAvailableSegments = function(jobId) {
      var dump = [];

      // queries for all segments
      return this.getSegments(jobId) //Return here
      .then(function(segments) {
        // dumps segments status in a temp array
        for (var i = 0; i < segments.length; i++) {
          dump.push(segments[i].status);
        }
        // returns true if there is an 'available' in the temp array
        if (dump.indexOf('available') >= 0) {
          return true; 
        } else {
          return false;
        }
      });
    };

since your success function returns true or false the promise from areThereAvailibleSegments-function will have that boolean.

Then in you controller:

SegmentsService.areThereAvailableSegments(jobId).then(function(available){
  if (available) {
      console.log('there are available segments, then trigger texteditor');
    } else {
      console.log('no segments to translate');
    }
});
Sign up to request clarification or add additional context in comments.

2 Comments

I would improve service to simply return return dump.indexOf('available') >= 0;, but other then that this is correct answer.
Thanks for this. I really like this approach, very clean.
2

Theres no reason to have both the service and factory. Remove the factory and move that code into the service. As for your problem, you will have to use the $q module to return a promise. https://docs.angularjs.org/api/ng/service/$q

this.areThereAvailableSegments = function(jobId) {
  var dump = [];
  var deferred = $q.deferred;

  // queries for all segments
  this.getSegments(jobId)
  .then(function(segments) {
    // dumps segments status in a temp array
    for (var i = 0; i < segments.length; i++) {
      dump.push(segments[i].status);
    }
    // returns true if there is an 'available' in the temp array
    if (dump.indexOf('available') >= 0) {
      deferred.resolve(true);
    } else {
      deferred.resolve(false);
    }
  });

  return deferred.promise;
};

You will then have to deal with the promise in the controller.

2 Comments

Original code was fine, but yours is not recommended deferred anti-pattern.
dfsq, curious to see why is this anti-pattern?
0

I would set it up like this...complete the promise in your factory and then call the data in the controller.

In your services js:

    .factory('SegmentsFactory', function ($resource) {

             var APIRequest = {};

            APIRequest.getSegments = function(id){
                    segmentsAPI = $resource('http://localhost:3000/api/v1/segments/'+id'', ;
                    return segmentAPI.get().$promise.then(function(segments){
                        console.log(segments);
                        return segments;
                    });
                };

        return APIRequest;
}]);

And then in your controller:

var dump = [];
        $scope.startJob = function() {

          if (SegmentsService.areThereAvailableSegments) {
             APIRequest.getSegments($scope.id).then(function(data){
                        if (data.error){
                            //error stuff here
                        } else {
                            if (data.segments.length > 0){
                                for(var i=0; i < segments.length; i++){
                                console.log(segments);
                                dump.push(segments[i].status);
        }

    };

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.