Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs: store data into variables from one factory call to use in an another factory call

I am totally new to angularjs and ionic. I try to store 4 values which i receive from a factory call into 4 variables which i need to use as parameters for my $http get call in another function:

here are the 4 variables defined at the beginning of my controller:

var firstId;
var firstTimestamp;
var lastId;
var lastTimestamp; 

I receive the data from the following call to my factory and assign the data to the variables:

ContentFactory.getAlbums().then(function(albums){

   $scope.albums = albums;

   firstId = albums.first_id;
   firstTimestamp = albums.first_timestamp;
   lastId = albums.last_id;
   lastTimestamp = albums.last_timestamp;
});

I try then to use these variables as parameters for another call to my factory (loadOlderAlbums is called in the view):

$scope.loadOlderAlbums = function(lastId, lastTimestamp) {
 ContentFactory.getOlderAlbums(lastId, lastTimestamp).then(function(albums){
    $scope.albums = $scope.albums.concat(albums);
    $scope.$broadcast('scroll.infiniteScrollComplete');
 });
};

The variables lastId & lastTimestamp are undefined when checking my factory:

getOlderAlbums : function(lastId, lastTimestamp){
        console.log(lastId); // lastId is undefined
        return $http.get('http://api.domain.xxx/albums/city/1? &direction=before&id=' + lastId + '&time=' + lastTimestamp + '&api_key=******&lang=en').then(function(response) {
                albums = response.data;
                return albums;
        });
    }

in the console i see the following error:

GET http://api.domain.xxx/albums/city/1?&direction=before&id=undefined&time=undefined&api_key=***&lang=en 500 (Internal Server Error)

because id & time are undefined.

here is the complete controller:

.controller('PicturesCtrl', function($scope, $ionicLoading,  ContentFactory) {

$scope.albums = [];

var firstId;
var firstTimestamp;
var lastId;
var lastTimestamp; 

ContentFactory.getAlbums().then(function(albums){
  $scope.albums = albums;
  firstId = albums.first_id;
  firstTimestamp = albums.first_timestamp;
  lastId = albums.last_id;
  lastTimestamp = albums.last_timestamp;

  // console.log(firstId);  -> values are correctly stored
  // console.log(firstTimestamp); -> values are correctly stored
  // console.log(lastId); -> values are correctly stored
  // console.log(lastTimestamp); -> values are correctly stored

 });


$scope.loadOlderAlbums = function(lastId, lastTimestamp) {
 ContentFactory.getOlderAlbums(lastId, lastTimestamp).then(function(albums){
    $scope.albums = $scope.albums.concat(albums);
    $scope.$broadcast('scroll.infiniteScrollComplete');
 });
};

})

and the factory:

.factory('ContentFactory', function($http) {

var albums = [];

return {



    getAlbums : function(){
        return $http.get('http://api.domain.xxx/albums/city/1?lang=en&api_key=***').then(function(response) {
                albums = response.data;
                return albums;
        });
    },


    getOlderAlbums : function(lastId, lastTimestamp){
        console.log(lastId); // lastId is undefined
        return $http.get('http://api.domain.xxx/albums/city/1?&direction=before&id=' + lastId + '&time=' + lastTimestamp + '&api_key=***&lang=en').then(function(response) {
                albums = response.data;
                return albums;
        });
    }
   }
})

Any help to figure out why the value of the variables are not passed to the second factory call are really appreciated. Its probably a logical issue as i'm a total newbie ;)

UPDATE:

Please find below a sample response which I received from my first $http GET call in Content.Factory.getAlbums

{
"city_id": 1,
"total_pages": 199,
"total_results": 3977,
"first_id": 15448,
"first_timestamp": 1437230820,
"results": [
    {
        "album_id": "15448",
        "title": "MAD pres. Splash The Madness Party at Aftermoon",
        "description": "The MAD crew is back and returned to Aftermoon for a night of Melbourne Bounce, Hip Hop, Big Room, Electro- and Progressive House. At the decks were Dam-Sib-Dao, Beatender, Madz Mellow & Odyszey, supported by Mc Ben10.",
        "photographer_id": "8",
        "photographer_name": "ploy",
        "image_count": "88",
        "cover_image": "5dd26da7f37da641d775568d2e98ae44.jpg",
        "date": "2015-07-18 21:47:00",
        "location_id": "705",
        "location_name": "Aftermoon"
    },
    {
        "album_id": "15446",
        "title": "Superhero Charity Party at KU DÉ TA Bangkok",
        "description": "KU DÉ TA Bangkok and the charity organisation \"Habitat for Humanity Thailand\" joined forces for the SUPERHERO “CHARITY PARTY\". Habitat for Humanity Thailand improves the quality of Thai people’s lives through building homes and transforming communities. Entry is Free, but there guests are invited to do a FREE-WILL DONATION of which 100% will go to Habitat for Humanity Thailand to build houses under the program “Make Her Day” in Pathumthani.",
        "photographer_id": "15",
        "photographer_name": "win",
        "image_count": "176",
        "cover_image": "742a0065d046418041175ff8005d7174.jpg",
        "date": "2015-07-18 18:46:00",
        "location_id": "809",
        "location_name": "KU DÉ TA"
    }
],
"last_id": 15427,
"last_timestamp": 1437062520
}

The $scope.loadOlderAlbums is called in my view (Ionic infinite scroll)

<ion-infinite-scroll
  on-infinite="loadOlderAlbums()"
  distance="1%">
</ion-infinite-scroll>
like image 702
Menelik Avatar asked Nov 09 '22 08:11

Menelik


1 Answers

Change your $scope method for fetching the older albums. You are expecting the parameters to be passed in - but by the time you call it they should already be defined in your controller scope, so there really isn't a need to do so.

And seeing as how you are not exposing the lastId or lastTImestamp onto this or $scope, that leads me to believe that in your view you are calling the method like so:

<button ng-click="loadOlderAlbums()">Load more albums</button>

In which case, lastId and lastTimestamp were never passed to your $scope function, and it just wont work.

Something like this should do it:

$scope.loadOlderAlbums = function() {
 ContentFactory.getOlderAlbums(lastId, lastTimestamp).then(function(albums){
    $scope.albums = $scope.albums.concat(albums);
    $scope.$broadcast('scroll.infiniteScrollComplete');
 });
};

Now, you won't have to pass lastId or lastTimestamp into your function (as you've previously required).


If you are certain that the getAlbums() call has fired before you start scrolling, the following should work just fine (redacted a bunch of code to highlight the main point):

var lastId, lastTimeStamp: 

getAlbums().then(function (res) {
  lastId = res.lastId;
  lastTimeStamp = res.lastTimeStamp;
});

/** Later, called from the view **/
function loadOlderAlbums () { // <-- Kill the arguments.
  $http.get('path' + lastId + lastTimeStamp).then(function (res) {
    /** woop woop! **/ 
  }); 
}

Otherwise - you would need to setup a failsafe for that scenario. You could either go with another promise, or something very simple like so for the time being to possibly rule it out:

var loaded = false;

function getAlbums () {
   $http.get('albums').then(function (res) {
     /** set the ID's and timestamps **/ 
     loaded = true;
   });
}

function loadOlderAlbums () {
  if (!loaded) {
    throw new Error('Yep, nope. We have to get some other albums first. Sorry!');
  }

  /** your old loadOlderAlbums implementation **/ 
}

By adding that throw statement in there, you can verify that there is:

  • Either a race condition in the storing(through getAlbums)/usage(through loadOlderAlbums) of the lastId and lastTimeStamp variables.
  • Or it magically started working.

Since you are using Ionic - which in turn uses ui-router; You could move the call to getAlbums into a resolve block and you would be ensured that the data is loaded into your controller before you start scrolling.

That could look something like:

.state('...', {
  controller: 'PicturesCtrl',
  resolve: {
    preloadedAlbums: function (ContentFactory) { // available for DI later into your controller.
      return ContentFactory.getAlbums().then(function (response) {
        return {
          albums:  response,
          firstId: response.first_id,
          firstTimeStamp: response.first_timestamp,
          lastId: response.last_id,
          lastTimeStamp: response.last_timestamp
        }; 
      });
    }
  }
}

Now, any time you try to navigate to said state - the resolve object will get its stuff before the controller gets invoked. As such, you are ensured that the data is present before the user starts scrolling away in the scope of your PicturesCtrl, and the parameters required for loadMoreAlbums will be there.

Your PicturesCtrl could now look something along the lines of:

.controller('PicturesCtrl', function ($scope, $ionicLoading,  ContentFactory, preloadedAlbums) {

  $scope.albums = preloadedAlbums;

  var lastId = preloadedAlbums.lastId;
  var lastTimeStamp = preloadedAlbums.lastTimeStamp; 
  /** etc **/ 
});

I would try out the first suggestion (with a throw) to ensure that it is a matter of the getAlbums promise not having been resolved that's causing your issue. Then I would consider moving onto a resolve block.


If that doesn't root out the cause of your problems, I would blast the codebase with debugger's and step through from start to finish to verify what is actually going on.

like image 56
Kasper Lewau Avatar answered Nov 15 '22 06:11

Kasper Lewau