(Angularjs) How to $http.get data and store it in service
Asked Answered
D

2

11

As you will see i'm new in AngularJS, JS and in web development at all =) really sorry for that but i try to.

I try to build a massive webform (about 200 different fields) with AngularJS controllers. I need access from controller to root data source. AngularJS team ask do not make Services just for storing data, but i want to make service for load and save data (at start into .json files on a server).

Service:

AppName.factory('MasterData', ['$rootScope', '$http', '$q', '$log', 
    function($rootScope, $http, $q, $log) {

    var responseData;
    $http.get('/getdata.php').then(function (response) {
        responseData = response.data;
        console.log(response.data);
    });

    return responseData;

}]);

Controller:

AppName.controller('justController', ['$scope', 'MasterData', '$log',
    function ($scope, MasterData, $log) {
        $scope.data = MasterData.justControllerSectionData;
        console.log(MasterData);
    }
]);

Controller return undefined. But console.log from service returns the object. I feel that the problem is too easy, but i can't find how to solve it :( Also i can't use function like .getData() from controller to service because it ask the data from server each time any controller loads. I have the routes in AngularJS app with 12-14 controllers (full webform divided by sections) and i think it is good to get the data from backend once.

P.S. I think there is problem with promises, but when i try to use code like this:

var defer = $q.defer();
$http.get('/getdata.php').success(function(data){
    defer.resolve(data);
});
return defer;

I've got object with resolve, reject and so on. And really can't understand what can i do with it :(

Help me to get the data in controller :)

Denunciate answered 29/1, 2014 at 22:31 Comment(1)
Form with 200 fields? You'd probably be much better off using Meteor and autoform: {{> quickForm collection="Books" id="insertBookForm" type="insert"}}Cyrilcyrill
B
18

Your code doesn't work, because the callback you supplied to success() in your service is called asynchronously; after your service has returned, that is:

The sequence is like this:

  1. The function in MasterData is run. The $http.get request is launched and attached the promise callback. responseData is referenced in this callback (aka. "closed over").
  2. The function returns from the service to your controller. responseData has not been set yet, which doesn't stop the parent scope function from returning.
  3. $http.get succeeds and responseData is set in the service however unreachable for the controller.

If the scoping of the nested function in success() is not clear to you, I'd recommend reading about closures in JavaScript (or even better, in general), for example here.

You can achieve your goal with a service like this:

function($q, $http, /* ... */) {    
    return {
        getData: function() {
            var defer = $q.defer();
            $http.get('/getdata.php', { cache: 'true'})
            .then(function(response) {
                defer.resolve(response);
            });

            return defer.promise;
    };
}

The $http service will happily cache your response data, so you don't have to. Note that you need to retrieve the promise from your deferred object to make this work.

The controller is like this:

/* omitted */ function($scope, YourService) {
    YourService.getData().then(function(response) {
        $scope.data = response.data;
    });
}

Since success is depreciated, I modified success to then.

Baroda answered 29/1, 2014 at 22:44 Comment(2)
I will read more about JS at all, thanks for the link. Your code returns the object with than, catch and finally methods, what should i do to get the data :) ?Denunciate
Silly me, added to the answer :).Baroda
E
1

Services should return the promise rather than the data. This is the asynchronous way.

First fetch the value in the Angular's run method. For this example I put it in the $rootScope since that makes it accessible to all scopes in all controllers.

AppName.run(['$http', '$rootScope',
    function($http, $rootScope) {
      console.log('Run');
      $http.get('http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo')
        .success(function(data) {
          $rootScope.resource = data;
          console.log($rootScope.resource);
        });
    }
  ])

This is not really necessary unless you store it in some weird place.

AppName.service('Resource',['$rootScope',
    function($rootScope) {
      return $rootScope.resource;
    }
  ]);

Every scope will inherit the values in the $rootScope (thats why the service really isn't necessary.

AppName.controller('mainController', ['$scope', 'Resource',
  function($scope, Resource) {
    console.log('controller');
    console.log(Resource);
  }
]);

Warning!!! This value will not be available until after the first controller loads. If you use it in the controller just remember that you can bind it to html but the first pass through the controller code will not have initialized the variable yet.

Etti answered 29/1, 2014 at 22:46 Comment(4)
If i right your solution will run $http.get every time when controller will load, so if i go forward to another controller and then get back to this one - i will run $http.get. I need to get the data once and store it in service.Denunciate
yes, true. To run only once, call it in the module.run() and store the value in a module.value() function;Etti
Yes, you just forgot, that i have 12 controllers that use the data from /getdata.php each on it's own $route in Angular. If i store the data in one controller i will not get the data in another controller. So i need the service. May be my logic at Angular app is bad ?Denunciate
Check out this plunker: plnkr.co/edit/aIMMoBd78hqTvtPeL2Nx?p=preview I will update my answer to address your issue.Etti

© 2022 - 2024 — McMap. All rights reserved.