3

I've created $http and REST API interface in AnguarJS service as a function that gets injected into different controllers like this:

// Global service to share between states
.service("appSharedService", ['$http', function($http) {
  // Method: Returns list of all cities.
  this.restCitiesGet = function() {
     return $http.get('http://example/nkhorasaniec7/api/v0/city');
  };

  // Method:
  this.citiesGet = function() {
    this.restCitiesGet().success(function (data) {
        console.log(data);
        return data;
      })
  };
}])

console.log(data); returns the right json output when I call citiesGet() .

// Main controller that prints list of cities.
.controller('CityList', ['$scope', function($scope, appSharedService) {
  $scope.cities = appSharedService.citiesGet();
  console.log($scope.cities);
}]);

This is my controller injecting my service. console.log($scope.cities); here returns undefined.

$scope.cities value doesn't get changed after route calls this controller.

Is there something wrong with my setup?

Something interesting is that after I change route and come back to this controller again, this time $scope.cities have my REST data and everything's fine.

I think there's something wrong with timing or asynchronous functionality problem here that I'm not aware of.

EDIT:

I could have had $http in my controller and this works all well:

.controller('CityList', ['$scope', '$http', function($scope, $http, appSharedService) {
  $http.get('http://localhost/nkhorasaniec7/api/v0/city').success(function (data) {
        $scope.cities = data;
      });
}]);

But I want to implement helper functions for this.

Pouya Sanooei
  • 1,012
  • 1
  • 13
  • 22

4 Answers4

3

I would say that the common approach would be to return the promise directly to the controller, much like you have mentioned above by directly using the http request.

// Global service to share between states
.service("appSharedService", ['$http', function($http) {


// Method: Returning the promise
this.citiesGet = function() {
  return $http.get('http://example/nkhorasaniec7/api/v0/city');

 };
}])

Controller:

  .controller('CityList', ['$scope', '$http', function($scope, $http, appSharedService) {
appSharedService.citiesGet().success(function (data) {
      $scope.cities = data;
    });
 }]);
Pouya Sanooei
  • 1,012
  • 1
  • 13
  • 22
Wottensprels
  • 3,307
  • 2
  • 29
  • 38
  • what if I want to have .success(function(data){} as a helper function in a service or factory. What's the correct approach or best practice here? – Pouya Sanooei May 20 '14 at 11:12
  • 1
    While I think there is one, I have not personally seen a widely accepted different approach. A service is returning a promise which then is handled by the controller. However, if you feel annoyed by that style, I recommend this lecture: http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/ – Wottensprels May 20 '14 at 11:15
1

I think you are right about the timing issue. From what I understand, you are getting a promise, that at the moment you do console.log($scope.cities) is not yet resolved.

If you use $scope.cities inside your page, you should see the results as soon as they are loaded. Another option would be to use the promise then function if you really want to log.

$scope.cities = appSharedService.citiesGet().then(function(data) {
  console.log(data);
  return data;
};
Federico Nafria
  • 1,397
  • 14
  • 39
  • Thank you, but the weird thing is $scope.cities doesn't change on first citiesGet() call, so the view binding that. I'll try to use 'then' to see if it makes an effect – Pouya Sanooei May 20 '14 at 11:41
1

Answering my own question:

I'm trying to make this happen in my a controller defined in my view using ng-controller, not a controller linked to a router (otherwise you could use resolve property like this Delaying AngularJS route change until model loaded to prevent flicker).

And I want to use REST using $http as a factory/service helper function for a cleaner code.

// Global service to share between states
.service("appSharedService", ['$http', '$q', function($http, $q) {
  this.citiesGet = function() {
      var deferred = $q.defer();
      $http({method: 'GET', url: 'http://localhost/nkhorasaniec7/api/v0/city'}).success(function(data) {
        deferred.resolve(data);
      }).error(function(data, status) {
        deferred.reject(data);
      });
      return deferred.promise;
  };
}])

I used angular $q promise here.

// Our main controller that prints list of cities.
.controller('CityList', ['$scope', 'appSharedService', function($scope, appSharedService) {
  var promise = appSharedService.citiesGet();
  promise.then(
        function(data){$scope.cities = data;}
        ,function(reason){alert('Failed: ' + reason);}
  );
}])

And used then function to use that promise. And now it always updates $scope.cities in any situation that template loads (not just in ng-view)

Community
  • 1
  • 1
Pouya Sanooei
  • 1,012
  • 1
  • 13
  • 22
-1

You can use $q service

.service("appSharedService", ['$http', '$q', function($http, $q) {
  // Method: Returns list of all cities.
  this.restCitiesGet = function() {

    var deffered = $q.defer();
     $http.get('http://example/nkhorasaniec7/api/v0/city').then(
      //success
      function(response){
         deffered.resolve(response.data);},
      //error
         deffered.reject();

      );
    return deffered 
  };

and after that you can use promise in you controller

    .controller('CityList', ['$scope', function($scope, appSharedService) {
        $scope.cities = []
       appSharedService.citiesGet().then(
        //success
        function(result){
        angular.copy(result, $scope.cities)
        console.log($scope.cities);
       }, 
      //error
      function(){
      console.log("load error");

});

    }]);
sylwester
  • 16,498
  • 1
  • 25
  • 33